I am working through "An Incremental Approach to Compiler Construction". Great fun. I started reading Lisp in Small Pieces (LiSP) a while back, but this paper is a more hands-on, tutorial approach; you're able to see the fruits of your labour sooner and measure your progress better. It would have helped more if I could have gotten my hands on the extended tutorial that is supposed to accompany the paper -- it seems to have disappeared from the web. Anyway, tackling the project with only the paper for company is more challenging.
Both this paper and LiSP use Lisp dialects as the source and implementation languages. While this has a number of advantages (can't imagine using C or C++ as the implementation language *shudder*), there is a minor drawback (I say minor because the benefits far outweigh the costs; most notably a) the joy of coding in Lisp and b) the obviation of the need for a parser) -- when you're writing regular Lisp code, you are juggling with the concepts of compile-time, run-time and read-time (to be fair, I haven't felt the need to concern myself with read-times yet), whereas when you're writing a Lisp compiler in a Lisp dialect, you now have two sets of these times to contend with -- one for your compiler and the other for your generated code. Moreover, there are transformations that convert the source expressions into simplified Lisp expressions in the source dialect, which adds to the fun and merriment. You are writing Lisp code that is acted upon by Lisp code, which then takes a quoted Lisp expression and produces another quoted Lisp expression, which is then acted upon by yet another piece of Lisp code which generates the equivalent assembly... As I said, great fun.
On second thoughts, I'm not sure whether the point about two sets of times (read/compile/execute) is really valid. The complexity mentioned at the end of the previous paragraph captures the situation more accurately.