a Programming Language in a Weekend. Here’s What I Learned.
**TL;DR:** I built *Arc*, a statically-typed programming language with a lexer, parser, type checker, and interpreter — all from scratch in Go. It took a weekend. Here’s what it taught me about how every programming language actually works.
—
## Why Would Anyone Do This?
I’ve been writing Python for 8 years. I use `if`, `for`, `def` every day. But I never really understood what happens between me typing `x = 10 + 20` and the computer knowing that `x` is `30`.
So I decided to find out — by building my own language.
## Meet Arc ⚡
Arc is a simple, statically-typed language. It looks like this:
“`
let name: string = “Dimple”
let age: int = 28
if age > 25 {
print(“experienced!”)
}
fn add(a: int, b: int) -> int {
return a + b
}
print(add(10, 20))
“`
Nothing fancy. But building it taught me more about computer science than any textbook.
## The Four Stages Every Language Goes Through
Here’s the secret: **every** programming language — Python, Go, Rust, JavaScript — does the same four things. They just do them differently.
### Stage 1: Lexing — Breaking Text into Words
When you write `let x: int = 10 + 20`, your computer doesn’t see code. It sees a string of characters:
“`
l-e-t- -x-:- -i-n-t- -=- -1-0- -+- -2-0
“`
The **lexer** is the first step. It scans this string and groups characters into meaningful chunks called *tokens*:
“`
[LET] [x] [:] [int] [=] [10] [+] [20]
“`
Each token gets a type tag. Is `x` a keyword or a variable name? Is `10` a number or a string? The lexer decides.
**The insight:** Spaces, newlines, and comments? Thrown away. They’re for humans, not computers.
### Stage 2: Parsing — Building a Tree
A flat list of tokens isn’t enough. Consider:
“`
let x: int = 10 + 20 * 3
“`
Is `x` equal to `90` or `70`? The tokens don’t tell you. But a **tree** does:
“`
=
/ \
x +
/ \
10 *
/ \
20 3
“`
Multiplication is deeper in the tree, so it runs first. `20 * 3 = 60`, then `10 + 60 = 70`.
**The tree structure *is* the order of operations.** This is called an Abstract Syntax Tree (AST), and the parser builds it using an algorithm called a *Pratt parser* — which handles operator precedence elegantly.
### Stage 3: Type Checking — The Safety Net
This is what makes Arc *statically* typed. Before any code runs, the type checker walks the tree and validates every operation:
“`
let x: int = 10
let y: string = “hello”
print(x + y) // ❌ ERROR: cannot use + with int and string
“`
In Python, this would crash at runtime — maybe after 20 minutes of execution, after writing half your data to the database. In Arc, it catches the bug in milliseconds, before anything runs.
The type checker uses a **symbol table** — essentially a dictionary that maps variable names to their types. When it sees `let x: int = 10`, it stores `{x: int}`. When it later sees `x + y`, it looks up both types and validates that `+` works with them.
**Functions create nested scopes:**
“`
┌─── Global Scope ──────────────┐
│ x → int │
│ ┌─── add() Scope ─────────┐ │
│ │ a → int │ │
│ │ b → int │ │
│ │ Can see: a, b, AND x │ │
│ └──────────────────────────┘ │
│ Cannot see: a, b │
└────────────────────────────────┘
“`
Inner scopes can see outer variables. Outer scopes can’t see inner ones. This is how *every* language handles scoping.
### Stage 4: Interpreter — Running the Tree
Finally, the interpreter walks the tree bottom-up and executes it:
1. Visit `20` → returns 20
2. Visit `3` → returns 3
3. Visit `*` → computes 20 × 3 = 60
4. Visit `10` → returns 10
5. Visit `+` → computes 10 + 60 = 70
6. Visit `=` → stores x = 70
That’s it. Solve the leaves, combine upward.
## What I Actually Learned
Building Arc in a weekend taught me things I’d never gotten from tutorials:
1. **Every language is the same machine.** Python, Go, Rust — they all lex, parse, check, and execute. The differences are in *what* they check and *how* they execute.
2. **Static typing isn’t about being strict — it’s about being fast.** Catching errors before runtime isn’t a limitation. It’s a feature that saves you at 3 AM.
3. **Trees are everywhere.** HTML is a tree. JSON is a tree. Your filesystem is a tree. Code is a tree. Once you see it, you can’t unsee it.
4. **Go is great for this.** Its simplicity forced me to think clearly. No magic, no metaprogramming — just structs, interfaces, and functions.
## Try It Yourself
Arc is open source: [**github.com/bBlazewavE/arc**](https://github.com/bBlazewavE/arc)
“`bash
git clone https://github.com/bBlazewavE/arc.git
cd arc
go build -o arc .
./arc examples/hello.arc
“`
Building a programming language sounds intimidating. It’s not. It’s four steps, each one simpler than the last. And once you build one, you’ll never look at code the same way again.
—






Leave a Reply