How do compilers work? Let’s invent a programming language to find out

Before code can run, a computer has to somehow turn human-readable text into instructions it can execute.

So in this video, we do exactly that from scratch. We invent a tiny programming language, write a compiler for it, and build a virtual machine that can actually run the programs we write.

You do not need to be an expert to follow along. The goal of this video is to make programming languages feel less mysterious by building one piece by piece, slowly and visually. If you have never written code before, you can watch this as a guided tour of how computers understand instructions. If you are technical, you will get a practical walkthrough of lexing, parsing, abstract syntax trees, bytecode, stack machines, function calls, local variables, and control flow.

A full table of contents is below, but at a high level we cover
1. What programming languages really are
2. How source code becomes tokens
3. How parsers turn text into structure
4. How math expressions, variables, and conditionals work
5. How functions are represented and executed
6. How compilers generate lower-level instructions
7. How a stack-based virtual machine runs code
8. Why computers do not “understand” code the way humans do

All the code written in this video: https://github.com/milen-patel/tinylang

Chapters:
00: 00: 00 – Introduction
00: 06: 20 – Basic Addition/Subtraction and Formalizing Our Intuition
00: 11: 30 – Viewing Expressions as Trees
00: 25: 17 – Evaluating Expression Trees Starts at the Bottom and Works Upward
00: 26: 20 – Throwing Multiplication/Division into the mix (remember PEMDAS?)
00: 29: 07 – How do we take a math expression and convert it to this new tree format
00: 33: 47 – Introduction to Context Free Grammars
00: 57: 32 – Formal Definition of a Context Free Grammar
01: 33: 48 – Simplifying Our Context Free Grammar for Math Expressions
01: 54: 40 – Real Programming Languages Have Context Free Grammars!
01: 58: 58 – A brief introduction to the Kotlin Programming Language
02: 23: 11 – Tokens, the building blocks of a programming language
02: 29: 07 – Splitting our input into tokens (Lexer Introduction)
02: 49: 28 – Testing our Lexer
03: 07: 26 – Commit 1 Checkpoint [Splitting an input program into tokens]
03: 08: 21 – How do we get our tokens into a tree (Parser)
03: 53: 50 – Testing our Parser
04: 29: 09 – Let’s Invent a Language to Compile Into (TAM)
05: 06: 32 – Commit 3 Checkpoint [Taking an input programming, and visualizing it as a tree]
05: 06: 37 – Walking over the tree to translate a program
05: 29: 20 – Making Recursion a Bit More Visible
05: 38: 45 – Commit 4, we have a compiler!
05: 39: 00 – Simulating the Machine to Run Compiled Programs
05: 46: 26 – Adding support for variables into our language
06: 24: 19 – Updating our target language to support variables
06: 42: 29 – Commits 6+7 [Constants into the programming language]
06: 44: 50 – But wait, we can’t update a variable
07: 07: 44 – Commit 8 [Making constants into variables]
07: 08: 15 – Comparison Operators (Less Than or Greater Than)
07: 33: 27 – Commit 8 [Add support for comparison of integers]
07: 33: 49 – If Statements and Conditional Logic
08: 11: 48- Commit 9 [Support for condition if statement]
08: 12: 43 – A brief interlude on loops
08: 17: 14 – Introducing Functions to our Language
08: 35: 20 – Adding new instructions to the machine to support functions
08: 45: 09 – Updating the Context Free Grammar to Support Functions
08: 54: 40 – Start Implementing Functions
10: 15: 15 – Writing a function in our new language
10: 24: 08 – Commit 10 [Support Functions]
10: 24: 37 – We cheated on implementing functions, let’s make it more realistic
10: 41: 59 – Function Patching
10: 58: 58 – Writing a programming that tests every feature of our language
11: 03: 56 – Visualizing Compilers and Programs
11: 22: 47 – An Apology to You
11: 24: 00 – Conclusion
11: 25: 26 – How to View the Code from this Video,
11: 28: 09 – Where I would go if I had more time