Not sure if this helps you. But let's define a macro A:
CL-USER 32 > (defmacro a (b c)
(print (list :macro-expansion b c))
(list 'print (list 'quote (list :runtime :b b :c c))))
A
This macro does two things: it prints something at macro expansion time and then generates some code it returns as a value.Now we can use this macro in some code:
CL-USER 33 > (defun test ()
(a 21 42))
TEST
If we now compile the function, the macro gets executed and prints something: CL-USER 34 > (compile 'test)
(:MACRO-EXPANSION 21 42)
TEST
NIL
NIL
We can also call the macroexpander independent of the compiler. The we a) get the side effect of the print statement and we see the result: CL-USER 35 > (macroexpand '(a 20 30))
(:MACRO-EXPANSION 20 30)
(PRINT (QUOTE (:RUNTIME :B 20 :C 30)))
T
At runtime we call the test function: CL-USER 36 > (test)
(:RUNTIME :B 21 :C 42) ; <- the printed output
(:RUNTIME :B 21 :C 42) ; print also returns its arg
Thus all the macro expansions have been done at compile time and we have generated some code there. No macro expansion at runtime.Thus it has to do with code generation and code execution at macro expansion - nothing about 'delaying' something.
The example isn't useful, but imagine a macro INFIX
(infix a * b + c)
which rewrites the expression to the Lisp expression: (+ (* a b) c)
There is nothing about 'delaying' -> it's just rewriting the form. Ideally at compile time. There are many other examples which do something different.