Let's make a Teeny Tiny compiler
web.eecs.utk.edu
web.eecs.utk.edu
I'm not affiliated with it, but I devoured it recently and thought it was amazing.
Appel's compiler implementation in x (if you choose anything other than ML you don't deserve to live) is a good follow on
I'd much rather have a language agnostic explanation that does not rely on certain classes or patterns that only work well in the word of OOP-Languages.
https://github.com/melling/ComputerLanguages/blob/master/com...
I suspect this is more due to the memory cost of storing a literal line of code more than the ROM or CPU cost of a lexer. Going back to the ZX-80 storing even the current line being typed as characters was probably a burden.
Alternatively, the Basic interpreter could store each line as a combination of tokens and characters. Commodore Basic did that (https://www.masswerk.at/nowgobang/2020/commodore-basic-renum...).
I also think none of the microcomputer Basics stored keywords as ascii strings. It would both slow down the interpreter and use more memory.
On another note, I went ahead and wrote a code emitter, because I couldn't wait for part three (though I noticed it's in the GitHub repo). Once you get this compiler up and running the sane thing to do is write a Makefile so you can treat Teeny as a first-class programming language. Just add a Makefile with the following:
%.c : %.tiny
<TAB>python3 ./teenytiny.py $< > $@
And you'll be able to type `make hello` and have hello.tiny compiled to hello.c compiled to hello automagically. I can never pass up an opportunity to remind people that Make is wonderful. LET nums = nums - 1
on purpose.First, a parser isn't a compiler, so this means you get through the whole first part of the course without having a compiler. By contrast, a code generator is a compiler, at least if there's some way to invoke it.
Second, although the theory of formal languages and parsers is complex, well-developed, and fascinating, it's not clear that it's important to building a compiler. People have written large systems in FORTH. MUMPS famously ran a number of hospital systems without being able to break a statement across lines. And a complete scannerless BNF grammar for S-expressions (without readmacros) is something like this:
<sexp> ::= <idchar>+ " "* | "(" " "* <sexp>* ")" " "*
And many, many huge systems have been written in Lisps whose grammars amount to little more than that.My favorite grammars are PEGs, which accommodate scannerless parsing rather better than LL(1) and LALR(1) parsers, because PEGs have infinite lookahead with worst-case linear-time parsing (at the expense, it must be admitted, of being a huge memory hog). PEGs are also composable in a way that LALR and LL grammars aren't. I wrote a one-page PEG parser generator targeting JS at https://github.com/kragen/peg-bootstrap/blob/master/peg.md a few years back.
But I think an excessive focus on the syntax of a programming language really detracts from what's really revolutionary about programming languages (and compilers), which is their semantics. We have much better theories of semantics now than we had in the 1970s when the traditional compilers course was being laid out. Books like Essentials of Programming Languages can be wonderful introductions to the fuzzier ones, and there's a lot of work in things like Coq and Idris to come up with tractable formalizations. But you don't need much of a theory of semantics to get a simple compiler up and running!
But I haven't ever actually taught a compilers course, so maybe my ideas are miscalibrated about what students would enjoy and find motivating (having a working compiler for a simple language after doing the first problem set) and what students will find difficult out of proportion to any rewards it might bring (getting recursive-descent parsers for complex grammars working, debugging precedence rules, refactoring grammars to eliminate left recursion, encountering unexpected exponential-time parse failures, etc.).
The focus on syntax in compilers literature is basically bike-shedding. People focus on syntax because they understand it, but code generation is much harder.
Matt Might's blog is a rare exception.
Do you have any resources on code generators?
Still, I don't think it's fair to say without qualification, "Code generation is much harder." Very simple code generation can be done by pasting together canned code fragments (the original meaning of "compiler") and occasionally computing and encoding a jump offset. A very simple code generator like the one in StoneKnifeForth https://github.com/kragen/stoneknifeforth is simpler than the parser needs to be for many popular languages. However, at least with my limited knowledge, it appears to me that parsing is a relatively closed-ended problem — sure, you can work hard to improve your error detection and recovery, and to give more useful error messages, but you're apparently going to get very little return for even enormous efforts at that. Optimization, on the other hand, which is part of code generation, is potentially arbitrarily complex, and you can keep getting good returns on your efforts for quite a long time.
So I would say that the easiest code generation is usually easier than the easiest parsing, unless you have the liberty to choose your language to make it easier to parse. But the hardest code generation is much, much harder than the hardest parsing. (Again, unless you're parsing a language deliberately designed to be difficult!)
Can you recommend any books for catching up?
Python is the first language I learned. So it's like this is being written in my native tongue.
I've read crafting interpreters, numerous times both before and after learning some Java basics, but seeing it in Python made it click.
Also love the way you used classes. That helped clarify some confusion I had with classes in Python.
Edit: s/N/nums
He removed that link from his comment and he apologized because it was the right thing to do. Also, virtue signaling to encourage bad behavior isn't right, no matter how good or superior you think it makes you feel. Especially considering you weren't smart enough to realize that I wasn't criticizing the referrals in his article ( valid ), I was criticizing the shortened link in his comment which was a referral.
Right and life went on before you decided to play "hero".
> They apologized because they were being civil and polite not out of some admission of guilt.
But you told him not to apologize. So that means you told him not to be civil? Also, it's not polite to speak for others as it isn't part of civil discourse.
> I think you can see what others thought of your tone as well.
Others? Just you. And it's rather childish to "appeal to downvotes".
> Both of your comments here come across as very angry.
"Yeah, no."
Listen, the only one causing drama here is you. I made one innocuous and helpful comment. The guy responded and everything was settled. The conversation had nothing to do with you and yet here you are.
My favourite demonstration of a "teeny tiny compiler" is still C4: https://news.ycombinator.com/item?id=8558822 Perhaps it inspired all these compiler articles?
I’d summarize my tutorial as a shorter version of Crenshaw’s.
I love seeing more and more compiler tutorials. They all have different goals and perspectives.
(...and meanwhile I get downvoted for making an observation.)