When running in the REPL (the most common way to interface with Lisp) the compile time and runtime Lisps are the same.
For example, here's a copy/paste from a REPL session that defines a macro that defines a function. The macro (at "compile time") prints information about the function (just its argument count) to stdout before using defun (itself a macro) to actually define the function.
Next I call the new function and print out a disassembly, just to show the function is in fact compiled
"CL-USER> " is the prompt in the REPL I use:
CL-USER> (defmacro my-defun (name arguments &body body)
(format t "Defining ~a, taking ~a arguments~%" name (length arguments))
`(defun ,name ,arguments ,@body))
MY-DEFUN
CL-USER> (my-defun omg-2 (value) (* value value))
Defining OMG-2, taking 1 arguments
OMG-2
CL-USER> (omg-2 34)
1156
CL-USER> (disassemble #'omg-2)
; disassembly for OMG-2
; Size: 33 bytes. Origin: #x52ED2714 ; OMG-2
; 14: 498B5D10 MOV RBX, [R13+16] ; thread.binding-stack-pointer
; 18: 48895DF8 MOV [RBP-8], RBX
; 1C: 488BD6 MOV RDX, RSI
; 1F: 488BFE MOV RDI, RSI
; 22: FF1425C0001052 CALL QWORD PTR [#x521000C0] ; GENERIC-*
; 29: 488B75F0 MOV RSI, [RBP-16]
; 2D: 488BE5 MOV RSP, RBP
; 30: F8 CLC
; 31: 5D POP RBP
; 32: C3 RET
; 33: CC10 INT3 16 ; Invalid argument count trap
NIL
CL-USER>
It is also possible to compile a Lisp program to an executable (or byte code or whatever) and run it, and not use the dynamic aspect of it.In an interpreter version of Lisp, the interpreter may expand the macros at runtime.
https://docs.racket-lang.org/guide/stx-phases.html
I understand there may be some philosophical or theoretical interest in framing Lisp the way you do, but does that invalidate the close-to-the-app thinking about the costs of macros and where those costs are distributed?
In SBCL, we would commonly say that code is "evaluated" rather than compiled or interpreted. This is because "compiler" is referring specifically to when assembly is emitted and "interpreter" is referring to when code is being used to call already compiled code. In the case of SBCL, it is doing both of these things simultaneously when it is reading a ".lisp" file. For example, if this is the contents of my .lisp file:
(defun boop () (print "hello"))
(boop) ;; prints hello
(defun scoop () (print "goodbye"))
The boop function is being evaluated (and therefore compiled), and then called on the next line. In traditional algol based languages like c and java with a separate compilation phase, it would not be possible to call boop before scoop has been turned into machine code.
So you are asking how the costs of evaluating lisp are distributed?
For one, the assembler generated whilst evaluating the .lisp file can be cached in a .fasl file so that it does not need to be evaluated again to be loaded into other projects.
Another is that macro's can be evaluated once during function definition. e.g.
(defmacro moob () `(print "kellog"))
(defun foo () (moob)) ; evaluates to (defun funky () (print "kellog"))
(defmacro moob () `(print "fish")) ; redefining moob
(foo) ; will print "kellog" because funky was evaluated before moob was redefined.
In this case, the cost of calling moob is trivial because it only occurs once during the definition of foo. It will slow down the initial load of your program, but no subsequent calls to foo.
SBCL has both an in-core compiler (compiles to machine code in memory) and a file compiler (compiles to machine code in an file).
SBCL also has an interpreter (relatively new and which usually is not used), which executes Lisp source as data.
What you do is, you use macroexpand-1, like so:
https://stackoverflow.com/questions/50754347/macro-with-a-li...
This is one of the big advantages of Lisp macros over C macros. In Lisp you write macros using Lisp, including normal user-defined functions. In C, on the other hand, you write macros using "C Preprocessor directives"