My personal path, a hobbyist, was focused at first on interpreters for Brainfuck, Scheme, lower-case forth, and lower-case lisp. I had a bit of "formal" undergraduate training in one PL course and one compilers course I took before I dropped out, but for the most part I hacked on stuff since then for fun and education.
After I was confident implementing some of those minimal languages, I moved on to minimal versions of Lua, JavaScript/TypeScript, Python, SQL and Go; varying between implementing AST interpreters, bytecode VMs and native-code compilers (via C, LLVM, and x86). Either using the language's first-party parser or implementing my own handwritten parser.
I have never implemented garbage collection myself (at best I've hooked into the host language's GC, similar to using libgc). I haven't implemented register allocation since the college compilers course. I haven't implemented JIT compilation. I haven't spent very much time targeting Windows or macOS or anything other than Linux/x86_64.
You might wonder why so many PL/compiler resources focus on languages like lower-case lisp. Basically, parsing more common languages is more tedious because they have more syntax. So it takes you longer to get a working language implementation compared to the sparse syntax of a lisp (or a forth).
Once you get the hang of it with a forth or lisp though it's easy to bring that skill to "normal" languages. Although you will then need to pick up a technique for handling operator precedence (discussed below).
These helped me out and I think are reasonable to recommend to others. The list is not long because I have not explored that many broad introductory texts. And many that I did (listed further below) I really didn't like.
First off, real world languages generally don't use parser generators. Parser generators are also harder to learn, and are another dependency and build step. So you can happily skip.
If you still want to learn how to use a parser generator, look at books that are otherwise not ones I recommend like Modern Compiler Implementation in Java/C/ML or at The Dragon Book.
You can pick up the basics of handwritten parsers from the items in the Introductory section above. The major complex part remaining is operator precedence. Even though I've implemented it a few times, I need to go and look up an explanation again every time.
Basically, look up Shunting Yard, Pratt Parsing, or Precedence Climbing. There were one or two pages that helped me out in particular but I can't find them at the moment.
Andy Chu of Oil Shell has a survey of various explanations that you may find useful.
Notes: The final chapter ends (oddly enough) with building a little forth implementation. This book is one of my favorite technical books.
Notes: A literate walkthrough of a lisp implementation in C.
Various blogs and pages I've enjoyed reading and/or found helpful.
I can't evaluate my own stuff objectively but this is my list so I'm going to share with you the various resources I've written on the broad subject of compilers and interpreters.
If you know of anything here or end up writing about one of these, let me know!
I haven't tried these out, but I commonly see them recommended.
As you dig further into compilers/interpreters maybe you want to check these out. I own them and try to browse them occasionally but overall I'm not a fan. If you like them, great!
Notes: I know some people like it but boy do I abhor both the style and content of this book. It is extremely complicated and the whimsical style just makes me angry. Maybe The Little Schemer is better since the topic is less complex. I'm scared to try it.
Notes: This one was the text for the compilers course I took in college and I loved that course. But ultimately I'm not sure this book is as good as some other resources listed above (in aggregate).
I threw together a page with a few of my favorite resources for learning and hacking on compilers/interpretershttps://t.co/741TDxDLEO pic.twitter.com/tErFu9sjdy
— Phil Eaton (@phil_eaton) January 4, 2023