In Lisp they don't. See Emacs Lisp, Common Lisp, ISLISP. A Lisp macro gets only passed some data and returns some data. There is nothing like an AST.
If we define a macro foo-macro and we call it:
(foo-macro ...)
then ... can be any data.Example:
(defmacro rev (&rest items)
(reverse items))
Above macro REV gets data passed and reverses it. The &rest list of ITEMS gets reversed. ITEMS is the list of source arguments in the macro call.We can then write:
(rev 1 2 3 4 +)
(rev (rev 10 n -) (+ a 20 b) (rev 30 a *) list)
Example: CL-USER 7 > (let ((n 4) (a 3) (b 2))
(rev (rev 10 n -)
(+ a 20 b)
(rev 30 a *)
list))
(90 25 -6)
There is no syntax tree. All the macro does, is to reverse the list of its source arguments.If I let the macro describe the thing it gets passed, then we get this:
CL-USER 8 > (defmacro rev (&rest items)
(DESCRIBE items)
(reverse items))
REV
CL-USER 9 > (rev 1 2 3 4 +)
(1 2 3 4 +) is a LIST
0 1
1 2
2 3
3 4
4 +
10
As a side effect, we can see that the macro gets a simple list passed. A list of numbers and a symbol ("symbol" is also a data type). Not text. Not an AST.Also data which is possibly not read, but computed by other code. We can compute the arguments of the macro REV and pass the thing to EVAL. Works the same as above.
CL-USER 10 > (eval (append '(rev) (loop for i from 1 to 4 collect i) '(+)))
(1 2 3 4 +) is a LIST
0 1
1 2
2 3
3 4
4 +
10
The macro only gets that data and can do anything with it. There is no idea of Abstract Syntax Tree: it does not need to be a tree, it does not need to be valid Lisp code and it carries no syntactical information. Generally, it also does not need to return valid Lisp code. To be computed all we need is that the evaluator eventually sees valid Lisp code, not the individual macro.In Lisp the "reader" by default only parses a data layer: symbolic expressions. EVAL, macros and other Lisp functionality gets mostly data passed. EVAL has to figure out, what (a b c) actually is as a program. It can traverse it in an interpreter or compile it. A compiler may internally create AST representations -> but that free to the implementation.
The Lisp language then typically is not defined over text syntax, but data syntax.
A Lisp interpreter processes during execution not text, but s-expressions. It's a "List Processor", not a Text Processor. Lisp not Texp. ;-) The function COMPILE gets an s-expression passed, not text.
Racket and Scheme have other macro systems.