I've always wondered how computers understand language. How do words and symbols turn into 0s and 1s, and then back into something we can read? The typical explanations about compilers and interpreters give us the basics, but I often ask myself: Do I really know how it works?
This question has pushed me to take on a new challenge: writing my own programming language.
Through this journey, I want to dive deeper into how programming languages are built and why they’re suited for specific tasks. This isn’t a tutorial—it’s an experiment. I’ll make mistakes, rewrite code, and explore, but by the end, I hope to truly grasp how it all works.
Let’s Begin!
First, let’s understand how a programming language gets converted into 0s and 1s. Think of writing code like creating a recipe in English, but computers only understand binary—0s and 1s. To bridge this gap, we use compilers or interpreters as translators.
When you write code, the compiler breaks it down into smaller parts, called tokens (like breaking a recipe into steps). It then checks if everything makes sense, ensuring the instructions are clear and valid. Finally, it translates these tokens into machine code—the binary language computers understand.
This process happens in layers:
Lexical Analysis: The code is read and broken down into tokens (like reading a recipe and picking out key instructions like "boil" or "chop").
Syntax Analysis: The tokens are organized into a structure (like checking if the steps in your recipe make sense—e.g., you can’t bake a cake before mixing the ingredients).
Semantic Analysis: This ensures everything has meaning—i.e., you’re not asking the computer to do something impossible (like trying to bake a cake at -200°C).
Code Generation: Finally, the high-level language is converted into machine code, or the 0s and 1s that the computer can execute (this is the point where your friend finally understands the recipe in their own language!).
What are Compilers and Interpreters?
A compiler takes your entire recipe, translates it all at once, and then hands it back fully ready for your friend to follow. The whole process happens upfront—fast execution, but it takes time to prepare the translation.
An interpreter, on the other hand, translates your recipe step-by-step as your friend cooks. This means your friend can start right away, but the process might be slower since each step needs translating before it's done.
In short: It’s all about trade-offs. Compilers provide speed by translating everything upfront, making them perfect for high-performance applications. Interpreters, on the other hand, offer flexibility by translating as you go, allowing for quick changes and testing—ideal for dynamic tasks like web scripting or debugging. Therefore we use compilers for speed and interpreters for flexibility.
What’s the Plan?
Now that we have a basic understanding of the fundamental terms used in programming, we can start planning our project structure, and to do that, here are some key questions to think about:
Question 1: What will our language do?
We’ll start small—our first language will handle basic tasks like declaring variables, simple math, and printing results. Later, we’ll expand it with more advanced features like functions and classes.
Question 2: Compiler or interpreter?
We’ll begin by building an interpreter. It’s simpler to understand, and once we’re comfortable, we can explore building a compiler.
Question 3: What language will we use to build our language?
To keep things straightforward, we’ll start with Python, but that may change as the project evolves.
And That’s It!
With this plan in mind, we’ll start writing parts of the language step by step in the next post. Till then, happy coding!