From: David Steuber
Subject: Macro concern and advice wanted
Date: 
Message-ID: <87r7sx7kwc.fsf@david-steuber.com>
As an exercises in learning to write Lisp macros, I'm working on the
following for generating XHTML (as part of a package I am naming
ds-xxml:

(defun lckey (k)
  "Return lower-case string representing symbol-name for k."
  (if (keywordp k)
      (string-downcase (symbol-name k))
      (error "~S is not a keyword to lckey." k)))

(defun print-tag-attrs (format-string tag attrs)
  (format t format-string (lckey tag)
          (mapcar (lambda (av)
                    (if (keywordp av)
                        (lckey av)
                        (identity av))) attrs)))

(defmacro xxml (tree)
  (macrolet ((local-form-maker ()                             ; captures local variables where expanded
               `(let ((code nil))
                  (dolist (form forms (nreverse code))
                    (cond ((atom form) (push `(format t "~A" ,form) code))
                          ((keywordp (first form)) (push `(xxml ,form) code))
                          ((listp (first form)) (push `(xxml ,form) code))
                          ((fboundp (first form)) (push form code)))))))
    (cond ((and (keywordp (first tree))
                (= 1 (list-length tree)))                     ; (:hr)
           `(progn
              (format t "<~A />~%" (lckey ,@tree))
              (values)))
          ((and (keywordp (first tree))
                (keywordp (second tree)))                     ; (:hr :line-thickness 4)
           (let ((tag (first tree))
                 (attrs (rest tree)))
             `(progn
                (print-tag-attrs "<~A~{ ~A=\"~A\"~} />~%" ,tag ',attrs)
                (values))))
          ((keywordp (first tree))                            ; (:p "Text and more text")
           ;; we need to deal with multiple forms following the keyword like
           ;; (:p "text" ((:a :href "link") "text") "more text" (fn args))
           (let ((tag (first tree))
                 (forms (rest tree)))
             `(progn
                (format t "<~A>" (lckey ,tag))
                ,@(local-form-maker)
                (format t "</~A>~%" (lckey ,tag))
                (values))))
          ((listp (first tree))                               ; ((:p :class "small") "small text")
           (let ((tag (first (first tree)))
                 (attrs (rest (first tree)))
                 (forms (rest tree)))
             `(progn
                (print-tag-attrs "<~A~{ ~A=\"~A\"~}>" ,tag ',attrs)
                ,@(local-form-maker)
                (format t "</~A>~%" (lckey ,tag))
                (values))))
          )))

(xxml (:hr))
(xxml (:hr :line-thickness 4))
(xxml (:p "Text and more text" (:br) "A new line of text"))
(xxml ((:a :href "http://www.example.com/") "Example.com"))
(xxml ((:p :class "small") "small text" (:br) "new line of small text"))
(xxml ((:p :class "small") 
         "Small text here." (:br) (+ 2 3) (princ (+ 2 3)) (terpri)
         "See " ((:a :href "http://www.example.com/") 
                 "Example.com") "For more"))
(xxml (:table (:tr (:td "data"))))

(xxml (:html 
       (:head (:title "DS-XXML Demo"))
       (:body 
        ((:h1 :align "center") "DS-XXML Demo")
        (:p "This is a simple demonstration of using a macro to produce "
            "XHTML from a Lisp source file.  There are at least six other "
            "HTML generators listed on "
            ((:a :href "http://www.cliki.net/") "CLiki")
            "so naturally I figured I would work on another one."))))

The function print-tag-attrs is a result of factoring.  I have a third
place not shown here where it is used.  The macrolet is also the
result of factoring.  As you can see it is used twice.  I'm fairly
confident that this is a case where variable capture is desirable.  It
makes the definition for xxml fit on one screen on my 12" PowerBook
G4.

I have a runtime efficiency question about the xxml macro.  Suppose
the list I pass to it is generated at runtime by some function call.
Am I going to take a nasty performance hit when the macro has to be
conditionally expanded based on its input, or is Lisp much cleverer
than that?  To my neophyte eyes, it looks like I have an implicit eval
call.

Would anyonce care to comment on my macro?  I'm sure there must be
some newbe mistakes in there.

If the input format looks anything like LHTML, it isn't a
coincidence.  I was inspired by it.  I haven't added special keywords
to the macro obviously.  It also doesn't do pretty printing yet (if I
can figure out an inexpensive way to do that I will add it).

As it stands now, it is possible to add function call forms that
generate printed output, so I don't see any need for keywords like
:princ & Co as used in LHTML.  I think some utility functions would be
useful though like something to entify a string and other helpers so
that it is less easy to generate badly formed XHTML.

I've spent the better part of the day on this and I'm kind of happy
that it even works at all.
             
-- 
An ideal world is left as an excercise to the reader.
   --- Paul Graham, On Lisp 8.1

From: Barry Margolin
Subject: Re: Macro concern and advice wanted
Date: 
Message-ID: <barmar-68EE0E.22584102062004@comcast.dca.giganews.com>
In article <··············@david-steuber.com>,
 David Steuber <·····@david-steuber.com> wrote:

> I have a runtime efficiency question about the xxml macro.  Suppose
> the list I pass to it is generated at runtime by some function call.
> Am I going to take a nasty performance hit when the macro has to be
> conditionally expanded based on its input, or is Lisp much cleverer
> than that?  To my neophyte eyes, it looks like I have an implicit eval
> call.

As far as I can tell, your macro doesn't handle this case at all.  It 
checks for a keyword in various places, and if none of the tests succeed 
the COND just exits, and your macro returns NIL.  Remember, a macro 
receives the unevaluated subforms as its arguments -- the only thing 
that gets evaluated automatically is the result of the expansion.

-- 
Barry Margolin, ······@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
From: David Steuber
Subject: Re: Macro concern and advice wanted
Date: 
Message-ID: <87zn7klcx4.fsf@david-steuber.com>
Barry Margolin <······@alum.mit.edu> writes:

> In article <··············@david-steuber.com>,
>  David Steuber <·····@david-steuber.com> wrote:
> 
> > I have a runtime efficiency question about the xxml macro.  Suppose
> > the list I pass to it is generated at runtime by some function call.
> > Am I going to take a nasty performance hit when the macro has to be
> > conditionally expanded based on its input, or is Lisp much cleverer
> > than that?  To my neophyte eyes, it looks like I have an implicit eval
> > call.
> 
> As far as I can tell, your macro doesn't handle this case at all.  It 
> checks for a keyword in various places, and if none of the tests succeed 
> the COND just exits, and your macro returns NIL.  Remember, a macro 
> receives the unevaluated subforms as its arguments -- the only thing 
> that gets evaluated automatically is the result of the expansion.

D'oh!  This is what happens when you don't test all desired input
cases.

I've reworked the xxml macro and I /may/ have answered my own
question.  I ended up using eval explicitly in the macro because I
couldn't figure out how else to get it to work.  I would love it if
there is a way to remove the eval.

Here is the new code and a few test cases:

(defun lckey (k)
  "Return lower-case string representing symbol-name for k."
  (if (keywordp k)
      (string-downcase (symbol-name k))
      (error "~S is not a keyword to lckey." k)))

(defun print-tag-attrs (format-string tag attrs)
  (format t format-string (lckey tag)
          (mapcar (lambda (av)
                    (if (keywordp av)
                        (lckey av)
                        (identity av))) attrs)))

(defmacro xxml (tree)
  (macrolet ((local-form-maker ()                             ; captures local variables where expanded
               `(let ((code nil))
                  (dolist (form forms (nreverse code))
                    (cond ((atom form) (push `(format t "~A" ,form) code))
                          ((keywordp (first form)) (push `(xxml ,form) code))
                          ((listp (first form)) (push `(xxml ,form) code))
                          ((fboundp (first form)) (push form code)))))))
    (cond
      ((not (listp tree))                                 ; variable representing list (we hope)
       (eval ``(xxml ,,tree)))
      ((and (keywordp (first tree))
            (= 1 (list-length tree)))                     ; (:hr)
       `(progn
          (format t "~&<~A />~%" (lckey ,@tree))
          (values)))
      ((and (keywordp (first tree))
            (keywordp (second tree)))                     ; (:hr :line-thickness 4)
       (let ((tag (first tree))
             (attrs (rest tree)))
         `(progn
            (print-tag-attrs "~&<~A~{ ~A=\"~A\"~} />~%" ,tag ',attrs)
            (values))))
      ((keywordp (first tree))                            ; (:p "Text and more text")
       ;; we need to deal with multiple forms following the keyword like
       ;; (:p "text" ((:a :href "link") "text") "more text" (fn args))
       (let ((tag (first tree))
             (forms (rest tree)))
         `(progn
            (format t "~&<~A>" (lckey ,tag))
            ,@(local-form-maker)
            (format t "</~A>~%" (lckey ,tag))
            (values))))
      ((listp (first tree))                               ; ((:p :class "small") "small text")
       (let ((tag (first (first tree)))
             (attrs (rest (first tree)))
             (forms (rest tree)))
         `(progn
            (print-tag-attrs "~&<~A~{ ~A=\"~A\"~}>" ,tag ',attrs)
            ,@(local-form-maker)
            (format t "</~A>~%" (lckey ,tag))
            (values))))
      ((fboundp (first tree))                             ; we got a function.  may it return a list.
       (eval `(let ((flarn (funcall #',(first tree) ,@(rest tree))))
                  `(xxml ,flarn)))))))

;;; some test cases

(xxml (:html 
       (:head (:title "DS-XXML Demo"))
       (:body 
        ((:h1 :align "center") "DS-XXML Demo")
        (:p "This is a simple demonstration of using a macro to produce "
            "XHTML from a Lisp source file.  There are at least six other "
            "HTML generators listed on "
            ((:a :href "http://www.cliki.net/") "CLiki")
            "so naturally I figured I would work on another one.")
        (:hr)
        ((:table :width "100%") 
         (:tr ((:td :width "50%" "Foo" "Bar") "David Steuber")
              ((:td :width "50%" :align "right") "Just another Lisp hacker"))))))

(defun xml-maker ()
  (let ((r (random 3)))
    (case r
      (0 '(:p "Text and more text" (:br) "A new line of text"))
      (1 '((:a :href "http://www.example.com/") "Example.com"))
      (2 '((:p :class "small") "small text" (:br) "new line of small text")))))

(xxml (xml-maker))

(defvar foo (xml-maker))
(xxml foo)

So can I get rid of eval?  I am fairly sure that I take a severe
runtime beating in the cases where xxml is called with a variable or
a form producing function as its input.  I haven't tested a case of a
form producing macro.  My hope is that a form producing macro would
look like the constant list example.

It may be worth it to rework the macro so that instead of returning an
empty values, I return a lambda form that contains the macro
expansion.  I think I can do that.  I haven't looked at that yet, but
the idea has been in the back of my mind.

If the laws of logic dictate that I must keep the evals, then I will
want to return lambdas if possible.  The lambdas will be compiled,
correct?  If that is the case, and for whatever reason I don't save
them, will they get garbage collected?

-- 
An ideal world is left as an excercise to the reader.
   --- Paul Graham, On Lisp 8.1
From: Dave Fayram
Subject: Re: Macro concern and advice wanted
Date: 
Message-ID: <38ff3d6c.0406031536.75897f9b@posting.google.com>
> It may be worth it to rework the macro so that instead of returning an
> empty values, I return a lambda form that contains the macro
> expansion.  I think I can do that.  I haven't looked at that yet, but
> the idea has been in the back of my mind.
> 
> If the laws of logic dictate that I must keep the evals, then I will
> want to return lambdas if possible.  The lambdas will be compiled,
> correct?  If that is the case, and for whatever reason I don't save
> them, will they get garbage collected?

One thing I don't understand about this, and forgive me if this is
obvious, but why are you making this a macro that you use like a
function? It seems to me like the entire system could be used like a
macro, and then special-cased to have a macro to pre-compile forms.

I am just a novice, so perhaps there is something I'm missing that's
obvious, but it seems like you're using macros as a kind of
optimization here (or perhaps to create new syntax, by removing the
need to quote the input list). I think this kind of thing is fine
indeed, but I see a lot of people warning that it's more trouble than
it's worth.

Especially since you are using it at runtime and suddenly get an
implicit eval, wouldn't it be far better to just make a function
(which will compile) and then use a macro for the special optimization
case?
From: David Steuber
Subject: Re: Macro concern and advice wanted
Date: 
Message-ID: <87d64fq1m8.fsf@david-steuber.com>
·········@lensmen.net (Dave Fayram) writes:

> One thing I don't understand about this, and forgive me if this is
> obvious, but why are you making this a macro that you use like a
> function? It seems to me like the entire system could be used like a
> macro, and then special-cased to have a macro to pre-compile forms.

I am going with a macro because I want to compile the XHTML producing
code.  That is when I have a list like this:

(:html
  (:head
    (:title "Title"))
  (:body
    (:h1 "Header One")
    (:p "Paragraph text")
    (:h1 "Header Two")
    (:p "Paragraph text")))

I end up with compiled code that emits the XHTML rather than
traversing the data structure each and every time I want to produce
the XHTML.  If the above list was quoted when passed into the
appropriate function, it would still work.  Effectivly I would have a
small interpreter instead of an embedded language for producing XHTML
templates that get compiled.

This is part optimization and part syntactic sugar or whatever it
would be called.

> I am just a novice, so perhaps there is something I'm missing that's
> obvious, but it seems like you're using macros as a kind of
> optimization here (or perhaps to create new syntax, by removing the
> need to quote the input list). I think this kind of thing is fine
> indeed, but I see a lot of people warning that it's more trouble than
> it's worth.

I am also a novice.  I'm just exploring here.  I do hope to produce
useful code.  That would be great.  However I do have an ulterior
motive and that is to gain insight into Lisp programming.  Even if I
throw away every line of code I wrote, I have at least learned
something.

> Especially since you are using it at runtime and suddenly get an
> implicit eval, wouldn't it be far better to just make a function
> (which will compile) and then use a macro for the special optimization
> case?

I am actually hoping that the implicit (currently very explicit) EVAL
can go away.  Even if it can't, I am hoping that runtime form
generation is the exceptional case, not the common one.

I have found a way to wrap the macro into another so that I can
generate functions (either at compile time or run time) to cache the
results of the macro call.  Basicly I'm wrapping the macro expansion
inside a LAMBDA expression.  It should not be necessary to compile and
compile again just because a dynamic list was passed to the macro
instead of a constant list.

The proof would be in the use of the code in a real application that
is large enough to find the design faults.

Anyway, it is not a lot of code, so it is easy to throw away should
the approach turn out to be totally misguided.

Sooner or later I will peek at other code that does similar things to
see what design path was choosen.  Right now that would feel kind of
like looking at the answers to a test.  I'm not in school, but I still
want to figure things out on my own.  That is part of the excercise.

That said, I would still love it if someone could show me how I can
loose the EVAL or at least demonstrate that this is some exceptional
case where EVAL is actually the right thing.

I used the EVAL because got myself to a point where the macro
expansion was returning a call to the macro, ie:

(XXML (:P "Some paragraph text"))

was the result of the expansion.  The desired expansion for that would
be something like:

(PROGN
  (FORMAT t "~&<~A>" (LCKEY :P))
  (FORMAT t "~A" "Some paragraph text")
  (FORMAT t "</~A>~%" (LCKEY :P))
  (VALUES))

EVAL gets me that at a price.

I'm sure there is a better solution, but I just can't see it.  It may
even be that the EVAL is a symptom of a deeper problem with my code.

One little optimization would probably be to replace that second
FORMAT with a PRINC.  I currently have all printed output going to
*standard-output* in the hope that I can dynamicly bind
*standard-output* to any stream.

I need to learn to explain myself with fewer words.

-- 
An ideal world is left as an excercise to the reader.
   --- Paul Graham, On Lisp 8.1
From: Tim Bradshaw
Subject: Re: Macro concern and advice wanted
Date: 
Message-ID: <fbc0f5d1.0406040410.5be99345@posting.google.com>
David Steuber <·····@david-steuber.com> wrote in message news:<··············@david-steuber.com>...

> 
> I am going with a macro because I want to compile the XHTML producing
> code.  That is when I have a list like this:
> 
> (:html
>   (:head
>     (:title "Title"))
>   (:body
>     (:h1 "Header One")
>     (:p "Paragraph text")
>     (:h1 "Header Two")
>     (:p "Paragraph text")))
> 

If you want to be able to do this - convert some source form to Lisp
which you can then evaluate, *and* you don't have the source form
until runtime, then you need something which is equivalent to EVAL:
there's no way round that.  And you have to live with the limitations
that this implies.  Consider the case where the source form *is* lisp,
for instance!

It's generally better to write these things as three parts - the
expansion part which produces something which can be evaluated, a
macro which uses this, and a function which can use it at runtime.

Something like this:

(defun lhtml->lambda (lhtml-forms)
  ;; this returns a list of the form (lambda () ...)
  ...)

(defmacro lhtml (&body forms)
   ;; this is for the normal LHTML-as-source case
  `(funcall #',(lhtml->lambda ',forms)))

(defun lhtml->function (lhtml-forms)
  ;; return a function which can be called.
  (compile nil (lhtml->lambda lhtml-forms))
From: David Steuber
Subject: Re: Macro concern and advice wanted
Date: 
Message-ID: <87r7svl47k.fsf@david-steuber.com>
··········@tfeb.org (Tim Bradshaw) writes:

> If you want to be able to do this - convert some source form to Lisp
> which you can then evaluate, *and* you don't have the source form
> until runtime, then you need something which is equivalent to EVAL:
> there's no way round that.  And you have to live with the limitations
> that this implies.  Consider the case where the source form *is* lisp,
> for instance!

I was thinking that.  I couldn't see a way around EVAL.  Frustrating
when the first thing you read is EVAL is EVIL.

As for my source, not only can it be lisp, it can have lisp embedded
in it.  I didn't show the example, but any one of the lists I gave as
examples could contain lisp that prints text or does other stuff like
quietly noting that it was evaluated.

It looks like the compiler macro is going to give me a clean
solution.

-- 
An ideal world is left as an excercise to the reader.
   --- Paul Graham, On Lisp 8.1
From: Joerg Hoehle
Subject: Re: Macro concern and advice wanted
Date: 
Message-ID: <uoemyvlpv.fsf@users.sourceforge.net>
David Steuber <·····@david-steuber.com> writes:

> ··········@tfeb.org (Tim Bradshaw) writes:
> 
> > [...] then you need something which is equivalent to EVAL:
> > there's no way round that.
> I was thinking that.  I couldn't see a way around EVAL.  Frustrating
> when the first thing you read is EVAL is EVIL.

Did you read closely enough?: Tim said "equivalent to". Evilness comes
from EVAL's (non-)behaviour with lexical environments. There's nothing
bad about something "equivalent to" eval.  Walking down any data
structure to generate something can be considered like interpretation
or evaluation of this data structure: it's EVAL, not EVIL!

> As for my source, not only can it be lisp, it can have lisp embedded
> in it.
Then you probably should try to avoid EVAL and use macro
transformations to carefully build a Lisp expression than contains
both the generatede parts and the original embedded code. That will
probably take care of lexical scoping.

E.g. (I invent freely)
(html (body (@ (bgcolor (:lisp (get-color)))) (h1 "Hi")))
might yield
(list "<html><body bgcolor=\"
      (get-color)
      "\"><h1>H1</h1></body></html>")
or whatever.

> It looks like the compiler macro is going to give me a clean
> solution.
Could you elaborate?
I ask because I believe normal macros could just be enough. E.g. some
people here posted macros named HTML which would just do the above
> sexp->string transformation.

Regards,
	Jorg Hohle
Telekom/T-Systems Technology Center
From: Tim Bradshaw
Subject: Re: Macro concern and advice wanted
Date: 
Message-ID: <fbc0f5d1.0407070936.160cb3ab@posting.google.com>
Joerg Hoehle <······@users.sourceforge.net> wrote in message news:<·············@users.sourceforge.net>...

> Did you read closely enough?: Tim said "equivalent to". Evilness comes
> from EVAL's (non-)behaviour with lexical environments. There's nothing
> bad about something "equivalent to" eval.  Walking down any data
> structure to generate something can be considered like interpretation
> or evaluation of this data structure: it's EVAL, not EVIL!

> [... elided ...]

> I ask because I believe normal macros could just be enough. E.g. some
> people here posted macros named HTML which would just do the above
> > sexp->string transformation.

I meant equivalent in the formal sense.  Normal macros are enough to
do something like SEXPy-html-with-embedded-lisp -> Lisp -> output
conversion if and *only if* you have the SEXPy-html-with-embedded-lisp
available to you at compile time (strictly, at macro-expansion time). 
If you don't, you have to make explicit use of an evaluator for the
embedded lisp, and if that lisp is common lisp, that means you have to
use something equivalent to EVAL.

The reason I said `equivalent to' is that (as you'll see below) my
system actually uses something which is closer to
  (funcall (compile nil '(lambda () ...)))
which is clearly equivalent to EVAL, although not actually EVAL.

All the HTML-generation macros I'm familiar with (which is
one-and-a-bit) rely on having the SEXPY-html available at
macro-expansion time and thus do not need to make explicit use of an
evaluator: they just intervene in the normal compilation process to
convert SEXPy-html into Lisp which is compiled and (later) evaluated
in the normal way.

However I have another system, DTML, which does something rather like
what the original poster wanted.  It reads files (or streams)
containing markup which can contain `macros' which have definitions
which are unconstrained Lisp.  For example:

<def :name doc-title
     :result "Guide to macro expansion">

<def :name mydoc
      ...
      :result `(html () (head () ...) ,@(if ... ... (unless ...
...)))>

<mydoc <title> ...>

The value of the :result attribute is just Lisp code.  Often, as in
DOC-TITLE above, it's rather trivial.  But, as with MYDOC, it can be
quite general: it can be completely general CL in fact.  And of course
none of it is known at compile time.

So what my system does when it sees one of these definitions is to
wrap it up in a suitably-crafted list beginning (LAMBDA ...), and then
compile that, storing the result in the `environment' for the document
it's processing.  Subsequent use of this DTML macro simply involves
calling the function retrieved from the environment.  It could
alternatively simply use EVAL, but DTML macros are generally used many
times (and in fact DTML has support for library files which only get
reloaded on change, and whose definintions persist in the document's
environment), so I don't.

But it does use something which is equivalent to EVAL, lexical
environment issues &c &c: sometimes that is needed.

--tim
From: Praki Prakash
Subject: Re: Macro concern and advice wanted
Date: 
Message-ID: <dca1034a.0407092026.5c480c8c@posting.google.com>
··········@tfeb.org (Tim Bradshaw) wrote in message news:<····························@posting.google.com>...
[deleted stuff]
> 
> All the HTML-generation macros I'm familiar with (which is
> one-and-a-bit) rely on having the SEXPY-html available at
> macro-expansion time and thus do not need to make explicit use of an
> evaluator: they just intervene in the normal compilation process to
> convert SEXPy-html into Lisp which is compiled and (later) evaluated
> in the normal way.
>
> However I have another system, DTML, which does something rather like
> what the original poster wanted.  It reads files (or streams)
> containing markup which can contain `macros' which have definitions
> which are unconstrained Lisp.  For example:
>
Is DTML available for download? Google didn't turn up anything.
 
Thanks,
Praki
[deleted stuff]
From: Joe Marshall
Subject: Re: Macro concern and advice wanted
Date: 
Message-ID: <ekovhbpp.fsf@comcast.net>
David Steuber <·····@david-steuber.com> writes:

> I am going with a macro because I want to compile the XHTML producing
> code.  That is when I have a list like this:
>
> (:html
>   (:head
>     (:title "Title"))
>   (:body
>     (:h1 "Header One")
>     (:p "Paragraph text")
>     (:h1 "Header Two")
>     (:p "Paragraph text")))
>
> I end up with compiled code that emits the XHTML rather than
> traversing the data structure each and every time I want to produce
> the XHTML.  If the above list was quoted when passed into the
> appropriate function, it would still work.  Effectivly I would have a
> small interpreter instead of an embedded language for producing XHTML
> templates that get compiled.
>
> This is part optimization and part syntactic sugar or whatever it
> would be called.

This calls for a COMPILER-MACRO.  You need two things: an
`interpreter' that walks list structure and emits the appropriate text
at runtime, and a `compiler' that notices at compile time the special
common case of having literal list structure.  The compiler-macro
would expand into the fast code when it could, but it would punt to
the runtime implementation if the structure were not available at
compile time.


-- 
~jrm
From: David Steuber
Subject: Re: Macro concern and advice wanted
Date: 
Message-ID: <873c5bmjoe.fsf@david-steuber.com>
Joe Marshall <·············@comcast.net> writes:

> > This is part optimization and part syntactic sugar or whatever it
> > would be called.
> 
> This calls for a COMPILER-MACRO.  You need two things: an
> `interpreter' that walks list structure and emits the appropriate text
> at runtime, and a `compiler' that notices at compile time the special
> common case of having literal list structure.  The compiler-macro
> would expand into the fast code when it could, but it would punt to
> the runtime implementation if the structure were not available at
> compile time.

Is this similar to what Barry Margolin said:

  "Write a function that can be used with evaluated data, and only use
  the macro when literal data is being provided."

I just now looked in the CLHS and see that there is a
DEFINE-COMPILER-MACRO and a section 3.2.2.1 that discusses them.
Cool.  Thanks.

I was thinking I would be defining the interpreter function and then
placing a call to that in the appropriate place in the macro's COND
form.  This sounds like a way to automate that for the programmer, so
to speak.

On Lisp doesn't mention compiler macros in the index. CLtL2 does and
has two pages devoted to the topic.

-- 
An ideal world is left as an excercise to the reader.
   --- Paul Graham, On Lisp 8.1
From: Edi Weitz
Subject: Re: Macro concern and advice wanted
Date: 
Message-ID: <87zn7j9w9l.fsf@bird.agharta.de>
On 04 Jun 2004 11:23:13 -0400, David Steuber <·····@david-steuber.com> wrote:

> I just now looked in the CLHS and see that there is a
> DEFINE-COMPILER-MACRO and a section 3.2.2.1 that discusses them.
> Cool.  Thanks.
>
> I was thinking I would be defining the interpreter function and then
> placing a call to that in the appropriate place in the macro's COND
> form.  This sounds like a way to automate that for the programmer,
> so to speak.
>
> On Lisp doesn't mention compiler macros in the index. CLtL2 does and
> has two pages devoted to the topic.

This URL has just been posted in another thread:

  <http://lecture.pentaside.org/paper/compilermacro-lemmens/compiler-macros-for-publication.lisp>

Edi.
From: Rahul Jain
Subject: Re: Macro concern and advice wanted
Date: 
Message-ID: <874qpq6e3v.fsf@nyct.net>
Edi Weitz <···@agharta.de> writes:

> This URL has just been posted in another thread:
>
>   <http://lecture.pentaside.org/paper/compilermacro-lemmens/compiler-macros-for-publication.lisp>

Which happens to contain an example of yet another HTML-generating
language. ;)

-- 
Rahul Jain
·····@nyct.net
Professional Software Developer, Amateur Quantum Mechanicist
From: David Steuber
Subject: Re: Macro concern and advice wanted
Date: 
Message-ID: <87r7ssabiy.fsf@david-steuber.com>
Rahul Jain <·····@nyct.net> writes:

> Edi Weitz <···@agharta.de> writes:
> 
> > This URL has just been posted in another thread:
> >
> >   <http://lecture.pentaside.org/paper/compilermacro-lemmens/compiler-macros-for-publication.lisp>
> 
> Which happens to contain an example of yet another HTML-generating
> language. ;)

Clearly they're aren't enough of those.

-- 
An ideal world is left as an excercise to the reader.
   --- Paul Graham, On Lisp 8.1
From: Marco Antoniotti
Subject: Re: Macro concern and advice wanted
Date: 
Message-ID: <F2Zwc.7$2i5.3664@typhoon.nyu.edu>
David Steuber wrote:
> Rahul Jain <·····@nyct.net> writes:
> 
> 
>>Edi Weitz <···@agharta.de> writes:
>>
>>
>>>This URL has just been posted in another thread:
>>>
>>>  <http://lecture.pentaside.org/paper/compilermacro-lemmens/compiler-macros-for-publication.lisp>
>>
>>Which happens to contain an example of yet another HTML-generating
>>language. ;)
> 
> 
> Clearly they're aren't enough of those.
> 

You mean that the one I just started 15 minutes ago with (defmacro html 
....) is useless? :)

Cheers

--
Marco
From: David Steuber
Subject: Re: Macro concern and advice wanted
Date: 
Message-ID: <4ddd570c.0406042019.4c2c0c2d@posting.google.com>
Edi Weitz <···@agharta.de> wrote in message news:<··············@bird.agharta.de>...
> On 04 Jun 2004 11:23:13 -0400, David Steuber <·····@david-steuber.com> wrote:
> 
> > I just now looked in the CLHS and see that there is a
> > DEFINE-COMPILER-MACRO and a section 3.2.2.1 that discusses them.
> > Cool.  Thanks.
> >
> > I was thinking I would be defining the interpreter function and then
> > placing a call to that in the appropriate place in the macro's COND
> > form.  This sounds like a way to automate that for the programmer,
> > so to speak.
> >
> > On Lisp doesn't mention compiler macros in the index. CLtL2 does and
> > has two pages devoted to the topic.
> 
> This URL has just been posted in another thread:
> 
>   <http://lecture.pentaside.org/paper/compilermacro-lemmens/compiler-macros-for-publication.lisp>

Looks like it is worth the read.  I couldn't figure out how to do a
compiler macro from the CLHS.  I gave up on that route and took a
different tack.  I mixed my metaphors and went with a function that
can interpret a Lisp form plus a macro that can compile a Lisp form. 
The macro punts to the function when it gets a runtime generated list.

I've posted the current state of my code here:

http://www.david-steuber.com/~david/Lisp/ds-xxml.tar.gz

With luck, I typed that in correctly.  The code is ASDF installable. 
Or at least it can be manually put in the right place for ASDF to load
it with require.  I tested that much under SBCL on my Mac.

I'm at the point now where I want to look at LHTML and company to see
how they do it.  What I have is not something I would really want to
write markup in.  It may serve as a foundation for a higher level
framework though.  Further experimentation is required.  There are
certainly possibilities with being able to manipulate Lisp lists to
produce HTML.
From: Joe Marshall
Subject: Re: Macro concern and advice wanted
Date: 
Message-ID: <aczhx5hj.fsf@ccs.neu.edu>
·····@david-steuber.com (David Steuber) writes:

> I couldn't figure out how to do a compiler macro from the CLHS.

You do this:

First, write the function:

(defun foo (string num)
  (concatenate 'string string (format nil "~d" num)))

Then the compiler-macro will have a form like this:

(define-compile-macro <name> (&environment env &whole original-form <args> ...)
  (if  <check if args allow expansion>
       <macro-expansion>
       original-form))

So if NUM is almost always a literal constant, but STRING almost never
is, you might write this:

(define-compiler-macro foo (&environment env &whole original-form string num)
  (if (numberp num)
      `(concatenate 'string ,string ,(format nil "~d" num))
       original-form))

Since I'm not using the &ENVIRONMENT ENV arg, I really don't need it
in this example.  But if I wanted to handle the case that NUM were a
symbol that named a DEFCONSTANT variable, I'd want to pass along ENV: 

(define-compiler-macro foo (&environment env &whole original-form string num)
  (if (and (symbolp num)
           (constantp num env))
      `(concatenate 'string ,string ,(format nil "~d" (symbol-value num)))
       original-form))
From: Joerg Hoehle
Subject: Re: Macro concern and advice wanted
Date: 
Message-ID: <u1xjorwqp.fsf@users.sourceforge.net>
Hi,

David Steuber <·····@david-steuber.com> writes:
> That said, I would still love it if someone could show me how I can
> loose the EVAL or at least demonstrate that this is some exceptional
> case where EVAL is actually the right thing.

Your case does not require EVAL.

Please try out these steps:
1. I believe your have read Graham's On Lisp. Go read again some parts
   of it (sorry, no time to look up more precise recommendations).
2. Study those existing xyML->code systems (e.g. Tim Bradshaw's IIRC
   is among them).

3. Write a (set of) *functions* that will turn input like
 (:p (function-to-produce-text))
into
 (PROGN
  (PRINC "<P>")
  (function-to-produce-text)
  (PRINC "</P>"))
No EVAL, no MACROLET.
At a first glance, it's an exercise in tree walking.

4. Then write a tiny macro named XXML that calls this function to
   produce the output of the macro, i.e. the code expansion.

- isn't there some cookbook entry about this already?

5. Test with examples containing CODE, not just string or list literals.
macroexpand or macroexpand-1 '(xxml ...)
or just call the function from step 2.

6. Test in compiled mode, e.g.
(defun foo (x) (xxml (:p (my-func x))))
(compile 'foo)
warnings will point at problems you did not see.
invoke (foo 123) -- does it work?

7. Post again if 3-6 doesn't work out for you :-)

8. A hint: the cost of walking down the list/xyML structure may be
   negligible in face of network I/O. I.e., a nice code generating
   macro maybe superfluous w.r.t performance, yet cool.

A much better performance improvement will presumably be achieved by
using buffered I/O. Don't write small pieces to a socket!

Regards,
	Jorg Hohle
Telekom/T-Systems Technology Center
From: Barry Margolin
Subject: Re: Macro concern and advice wanted
Date: 
Message-ID: <barmar-F5FF96.17332303062004@comcast.dca.giganews.com>
In article <··············@david-steuber.com>,
 David Steuber <·····@david-steuber.com> wrote:

> Barry Margolin <······@alum.mit.edu> writes:
> 
> > In article <··············@david-steuber.com>,
> >  David Steuber <·····@david-steuber.com> wrote:
> > 
> > > I have a runtime efficiency question about the xxml macro.  Suppose
> > > the list I pass to it is generated at runtime by some function call.
> > > Am I going to take a nasty performance hit when the macro has to be
> > > conditionally expanded based on its input, or is Lisp much cleverer
> > > than that?  To my neophyte eyes, it looks like I have an implicit eval
> > > call.
> > 
> > As far as I can tell, your macro doesn't handle this case at all.  It 
> > checks for a keyword in various places, and if none of the tests succeed 
> > the COND just exits, and your macro returns NIL.  Remember, a macro 
> > receives the unevaluated subforms as its arguments -- the only thing 
> > that gets evaluated automatically is the result of the expansion.
> 
> D'oh!  This is what happens when you don't test all desired input
> cases.
> 
> I've reworked the xxml macro and I /may/ have answered my own
> question.  I ended up using eval explicitly in the macro because I
> couldn't figure out how else to get it to work.  I would love it if
> there is a way to remove the eval.

EVAL in macros is almost always wrong.  The evaluation will happen at 
compile time, not run time, so it won't see the run-time variable or 
function bindings.  And if the expression contains any local variables, 
they won't be accessible at all -- EVAL uses the global environment, not 
the lexical environment.

-- 
Barry Margolin, ······@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
From: David Steuber
Subject: Re: Macro concern and advice wanted
Date: 
Message-ID: <87ise7q3te.fsf@david-steuber.com>
Barry Margolin <······@alum.mit.edu> writes:

> EVAL in macros is almost always wrong.  The evaluation will happen at 
> compile time, not run time, so it won't see the run-time variable or 
> function bindings.  And if the expression contains any local variables, 
> they won't be accessible at all -- EVAL uses the global environment, not 
> the lexical environment.

I read this as the macro is not likely to work in a real application
even though my test cases show it working.  The EVAL got in there
because I found myself unable to do it another way.  I'm sure that
this is due to my lack of Lisp programming experience.  It looks like
such a simple thing, but I am stumped.

I know the EVAL is a red flag.  I just don't know how to reproduce the
functionality without it.  Any clues on how to get rid of the EVAL
would be most appreciated.

Alternatively, and I'm not thrilled about the idea, would be to drop
the idea of being able to compile XHTML producing forms and simply use
a function to interpret the data structure.

-- 
An ideal world is left as an excercise to the reader.
   --- Paul Graham, On Lisp 8.1
From: Barry Margolin
Subject: Re: Macro concern and advice wanted
Date: 
Message-ID: <barmar-68C4C7.08105704062004@comcast.dca.giganews.com>
In article <··············@david-steuber.com>,
 David Steuber <·····@david-steuber.com> wrote:

> Barry Margolin <······@alum.mit.edu> writes:
> 
> > EVAL in macros is almost always wrong.  The evaluation will happen at 
> > compile time, not run time, so it won't see the run-time variable or 
> > function bindings.  And if the expression contains any local variables, 
> > they won't be accessible at all -- EVAL uses the global environment, not 
> > the lexical environment.
> 
> I read this as the macro is not likely to work in a real application
> even though my test cases show it working.  The EVAL got in there
> because I found myself unable to do it another way.  I'm sure that
> this is due to my lack of Lisp programming experience.  It looks like
> such a simple thing, but I am stumped.
> 
> I know the EVAL is a red flag.  I just don't know how to reproduce the
> functionality without it.  Any clues on how to get rid of the EVAL
> would be most appreciated.

Write a function that can be used with evaluated data, and only use the 
macro when literal data is being provided.

-- 
Barry Margolin, ······@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
From: David Steuber
Subject: Re: Macro concern and advice wanted
Date: 
Message-ID: <878yf3mkpn.fsf@david-steuber.com>
Barry Margolin <······@alum.mit.edu> writes:

> > I know the EVAL is a red flag.  I just don't know how to reproduce the
> > functionality without it.  Any clues on how to get rid of the EVAL
> > would be most appreciated.
> 
> Write a function that can be used with evaluated data, and only use the 
> macro when literal data is being provided.

Hmmm.  That seems simple enough.  I imagine I can put this function
inside the macro in place of the eval forms to handle those
conditions.

-- 
An ideal world is left as an excercise to the reader.
   --- Paul Graham, On Lisp 8.1
From: Raymond Wiker
Subject: Re: Macro concern and advice wanted
Date: 
Message-ID: <86fz9buzmq.fsf@raw.grenland.fast.no>
David Steuber <·····@david-steuber.com> writes:

> Barry Margolin <······@alum.mit.edu> writes:
>
>> > I know the EVAL is a red flag.  I just don't know how to reproduce the
>> > functionality without it.  Any clues on how to get rid of the EVAL
>> > would be most appreciated.
>> 
>> Write a function that can be used with evaluated data, and only use the 
>> macro when literal data is being provided.
>
> Hmmm.  That seems simple enough.  I imagine I can put this function
> inside the macro in place of the eval forms to handle those
> conditions.

        Or possibly use a function plus a compiler macro?

-- 
Raymond Wiker                        Mail:  ·············@fast.no
Senior Software Engineer             Web:   http://www.fast.no/
Fast Search & Transfer ASA           Phone: +47 23 01 11 60
P.O. Box 1677 Vika                   Fax:   +47 35 54 87 99
NO-0120 Oslo, NORWAY                 Mob:   +47 48 01 11 60

Try FAST Search: http://alltheweb.com/
From: Edi Weitz
Subject: Re: Macro concern and advice wanted
Date: 
Message-ID: <871xkviud8.fsf@bird.agharta.de>
On 04 Jun 2004 01:39:57 -0400, David Steuber <·····@david-steuber.com> wrote:

> Alternatively, and I'm not thrilled about the idea, would be to drop
> the idea of being able to compile XHTML producing forms and simply
> use a function to interpret the data structure.

Sorry, I've not been following the whole thread but have you looked at
the various HTML producing macros[1] floating around?

Cheers,
Edi.


[1] Some examples:

    <http://www.tfeb.org/lisp/hax.html#HTOUT>
    <http://opensource.franz.com/aserve/aserve-dist/doc/htmlgen.html>
    <http://www.nicklevine.org/play/xhtml.lisp>
    <http://www.cliki.net/LML2>
    <http://weitz.de/cl-who/>
From: David Steuber
Subject: Re: Macro concern and advice wanted
Date: 
Message-ID: <87wu2nl4i3.fsf@david-steuber.com>
Edi Weitz <···@agharta.de> writes:

> Sorry, I've not been following the whole thread but have you looked at
> the various HTML producing macros[1] floating around?

Not yet.  Maybe I'm following the wrong approach, but I'm doing this
as much for the learning exercises as for the end product.  I know
researchers tend to hunt for existing literature on a given topic.
Perhaps that's what I should do as well before reinventing the wheel
again.

As I grow more comfortable with the facilities Lisp, I will switch
over to that method so that I can stand on other people's shoulders.

This thread has caused me to be introduced to the topic of compiler
macros.  I don't think I would have necessarily stumbled on that
without feedback.  Not without doing a cover to cover read of the CLHS
or perhaps even CLtL2.  No offense to the authors of those texts, but
that is some dry reading.  I do hit up the CLHS for reference rather
frequently though.

-- 
An ideal world is left as an excercise to the reader.
   --- Paul Graham, On Lisp 8.1