I'm trying to get a deeper understanding of some compiler-related resources (textbooks, blogs, etc.) so I'll be writing up some short reflections while I'm at the Recurse Center. One resource I see mentioned a lot is Language Implementation Patterns. The book introduces common approaches to common compiler steps. My current focus is on the middle of compilers — implementing passes on an intermediate representation — which happens to map neatly to part 2 of the book: analyzing languages.
Part of why I started my readings with this section of this book is because I'm working on a code generator for Abstract Syntax Description Language (ASDL) and I was feeling a little stuck on it. Worse, I felt I was missing the vocabulary to describe why I felt stuck. Language Implementation Patterns delivered. I felt validated when I came across things in the text I'd tried independently and I was able to adapt new concepts to my own project. I'll talk a bit more about ASDL in a future post, but it's useful to know that it's pretty simple. There can be forward references (requiring a symbol table, described in chapters 6 and 7) but didn't require implementing scopes. This helped keep the scope of my work manageable while still moving my compiler knowledge foundation a little higher.
One odd realization I had was that I was glad that I wasn't using any of the technologies used in the book. One of the advantages of the book is that it provides a thorough guide to using ANTLR to implement the book's techniques. Instead, I was working with Rust instead of Java and tree-sitter instead of ANTLR. These tools seemed to have just enough distance between them that it was a manageable but non-trivial exercise to improve my code. I was also able to see an example of some functionality in ANTLR (which is much more feature-rich than I thought) and then immediately have the words to look for alternative solutions elsewhere. As for the Rust side of things, I mostly read through the syn crate to see how they implemented various language constructs in Rust.
Aside from those improvements to my code, my big takeaway was an appreciation for the breadth offered by Language Implementation Patterns. Topics are briefly introduced before getting to the Java and ANTLR examples, which I felt were the main focus. However, there are plenty of resources that cover the actual substance of different compiler passes. What this book did well was explain each approach enough that I feel confident about where my next steps should take me.