That is, a Lisp program is represented as a list, a data structure that can itself be manipulated by a Lisp program. This means that a Lisp program can contain macros that can generate Lisp code at compile time (or even at runtime).
The latter (about Lisp macros) is actually orthogonal to it be homoiconic. What it does mean is that parsing, manipulating, and generating Lisp code is just like generating any other Lisp data structure. But there is nothing stopping non homoiconic languages from adding first-class macros (macros that are written in the language itself).
But there is nothing stopping non homoiconic languages from adding first-class macros (macros that are written in the language itself).
That is true in theory. However, I struggled with camlp5 [1] for hours to achieve something that could be done trivially with a lisp macro. I'm certain its because the I was having to do much more than rewrite a datastructure. Interestingly, it was this very experience that moved me back to lisp.
Zak mentioned Nemerle, which appears to have a very simple macro syntax. It actually reminds me of Lisp, although I'm sure Lispers will happily explain how its completely different. :-)
Have you taken a look at Nemerle? What there is broken compared to Lisp?
But there is nothing stopping non homoiconic languages from adding first-class macros
This is true, and Nemerle is an example of a language with this feature. That said, it's harder to write macros in non-homoiconic languages. One can write Lisp macros by starting with the desired output and inserting quote and unquote where appropriate. Generating a syntax tree in other languages requires mentally translating them to something like Lisp.
The latter (about Lisp macros) is actually orthogonal to it be homoiconic. What it does mean is that parsing, manipulating, and generating Lisp code is just like generating any other Lisp data structure. But there is nothing stopping non homoiconic languages from adding first-class macros (macros that are written in the language itself).