For modern compiler and a more direct approach I recommend https://www.cs.cornell.edu/~asampson/blog/llvm.html
Revisiting "Let's Build a Compiler"
11–20 of 53 posts
Re: Revisiting "Let's Build a Compiler"
#12Earlier quoted context omitted.
When I need to parse something nowadays I always end up with parser combinators. They just make so much sense.
What language do you use parser combinators in, and what kind of grammar do you parse usually? Nom was terribly verbose and unergonomic even by Rust's standards. Haskell's Megaparsec/Parsec is good but yeah, it's Haskell, you need to handle multiple monads (Parser itself is monadic, then your AST state, and maybe some error handling) at once and that's where I got confused. But I appreciated the elegance. I experimen…
Re: Revisiting "Let's Build a Compiler"
#13Earlier quoted context omitted.
When I need to parse something nowadays I always end up with parser combinators. They just make so much sense.
What language do you use parser combinators in, and what kind of grammar do you parse usually? Nom was terribly verbose and unergonomic even by Rust's standards. Haskell's Megaparsec/Parsec is good but yeah, it's Haskell, you need to handle multiple monads (Parser itself is monadic, then your AST state, and maybe some error handling) at once and that's where I got confused. But I appreciated the elegance. I experimen…
For Rust I used Nom and I didn't mind it all that much although I noticed it's quite baroque. If I had more to write I'd probably make some wrappers or macros of my own for most commonly used Nom snippets.
Re: Revisiting "Let's Build a Compiler"
#14Is "syntax-directed translation" just another term for a single-pass compiler, e.g. as used by Lua (albeit to generate bytecode instead of assembly / machine code)? Or is it something more specific?
> in the latter parts of the tutorial it starts showing its limitations. Especially once we get to types [...] it's easy to generate working code; it's just not easy to generate optimal code
So, using a single-pass compiler for a statically-typed language makes it difficult to apply type-based optimizations. (Of course, Lua sidesteps this problem because the language is dynamically typed.)
Are there any other downsides? Does single-pass compilation also restrict the level of type checking that can be performed?
Re: Revisiting "Let's Build a Compiler"
#15> Rather than getting stuck in front-end minutiae, the tutorial goes straight to generating working assembly code, from very early on Good summary. I had no background in compilers or related theory but read Jack Crenshaw's Let's Build a Compiler tutorials some time ago. My main take away from reading half a dozen or so of these tutorials was that building a simple compiler for a toy language was a small project that…
Re: Revisiting "Let's Build a Compiler"
#16> Jack Crenshaw's tutorial takes the syntax-directed translation approach, where code is emitted while parsing, without having to divide the compiler into explicit phases with IRs. Is "syntax-directed translation" just another term for a single-pass compiler, e.g. as used by Lua (albeit to generate bytecode instead of assembly / machine code)? Or is it something more specific? > in the latter parts of the tutorial it…
A sigle pass compiler can still split the various phases, and only do the code generation on the last phase.
Re: Revisiting "Let's Build a Compiler"
#17For modern compiler and a more direct approach I recommend https://www.cs.cornell.edu/~asampson/blog/llvm.html
LLVM makes it so much easier to build a compiler - it's not even funny. Whenever I use it, I feel like I'm just arranging some rocks on a top of a pyramid.
Re: Revisiting "Let's Build a Compiler"
#18> Jack Crenshaw's tutorial takes the syntax-directed translation approach, where code is emitted while parsing, without having to divide the compiler into explicit phases with IRs. Is "syntax-directed translation" just another term for a single-pass compiler, e.g. as used by Lua (albeit to generate bytecode instead of assembly / machine code)? Or is it something more specific? > in the latter parts of the tutorial it…
I'm actually a big fan a function-by-function dual-pass compilation. You generate IR from the parser in one pass, and do codegen right after. Most intermediate state is thrown out (including the AST, for non-polymorphic functions) and you move on to the next function. This give you an extremely fast data-oriented baseline compiler with reasonable codegen (much better than something like tcc).
Re: Revisiting "Let's Build a Compiler"
#19I think this is important and for a more sophisticated compiler design I find Ghuloum approach very appealing [1]. I.e. build a very simple subset of the language from top to bottom and then grow the meat gradually.
The really great book following this approach I've discovered recently was [2]. Although I find both C and x86 not the best targets for your first compiler, still a very good book for writing your first compiler.
[1] http://scheme2006.cs.uchicago.edu/11-ghuloum.pdf
[2] https://norasandler.com/2024/08/20/The-Book-Is-Here.html
Re: Revisiting "Let's Build a Compiler"
#20Earlier quoted context omitted.
When I need to parse something nowadays I always end up with parser combinators. They just make so much sense.
What language do you use parser combinators in, and what kind of grammar do you parse usually? Nom was terribly verbose and unergonomic even by Rust's standards. Haskell's Megaparsec/Parsec is good but yeah, it's Haskell, you need to handle multiple monads (Parser itself is monadic, then your AST state, and maybe some error handling) at once and that's where I got confused. But I appreciated the elegance. I experimen…