Programming Language Concepts - Comprehensive Guide
Table of Contents
- Core Programming Concepts
- Compile Time vs Runtime
- Interpreters
- V8 Engine
- JIT Compilation
- Python vs JavaScript Engines
- Universal Engine Pipeline
Shared from "Study" on Inkdown
Think of programming languages as recipes for a computer:
Interpreted Languages (Python, JavaScript):
# You write this:
print("Hello World")
# The computer needs an "interpreter" (like a translator)
# to read and execute line by line at runtimeCompiled to Native Binaries (C, Rust, Go):
// You write this:
printf("Hello World");
// Compiler converts it to MACHINE CODE:
// 01001000 01100101 01101100 01101100 01101111What is it? Automatic memory cleanup. When you create variables, they use RAM. GC automatically frees that memory when no longer needed.
# Python (has GC):
x = "hello world" # Memory allocated
x = None # Old string automatically cleaned up by GCLanguages WITH GC: Python, JavaScript, Java, Go Languages WITHOUT GC: C, C++, Rust
Why no GC?
Think of it like building a house vs living in it:
This happens before the program runs. The compiler checks your code, finds errors, and converts it into executable form.
Your Code (.rs, .c, .go)
↓
[COMPILER does its work] ← THIS IS COMPILE TIME
↓
Binary (.exe) — ready to runThis is when you execute the compiled binary. The program is doing its actual job.
Double-click .exe → Program runs → Shows output
↑
THIS IS RUNTIME# PYTHON (Interpreted - different flow)
x = 10
y = 0
result = x / y # CRASH at RUNTIME
# No compile time for Python!
# Error happens when line executes// C (Compiled)
int main() {
int x = 10;
int y = 0;
int result = x / y; // COMPILES fine (no syntax error)
// But CRASHES at RUNTIME (divide by zero!)
return 0;
}// RUST (Compiled with strict checks)
fn main() {
let x = 10;
let y = 0;
let result = x / y; // COMPILE ERROR!
// Rust warns: "this operation will panic at runtime"
// Error caught BEFORE running!
}| Compile Time | Runtime |
|---|---|
| Check syntax | Execute instructions |
| Type checking | User input |
| Memory analysis | Network requests |
| Error code generation | File reading/writing |
| Optimize code | Database queries |
| Produce binary | Crash if bug exists |
| Stage | Cooking Analogy | Programming |
|---|---|---|
| Compile Time | Reading recipe, checking ingredients, prepping kitchen | Compiler reads code, checks for errors, builds executable |
| Runtime | Actually cooking and eating | Program runs, interacts with user, does real work |
WRITE CODE RUN PROGRAM
↓ ↓
[Check syntax] [User clicks app]
↓ ↓
[Check types] [Read input]
↓ ↓
[Optimize] [Calculate result]
↓ ↓
[Build binary] [Show output]
↓ ↓
✅ Compile Time Done ❌ Crash if bug┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 1. EDITING │ ──▶ │ 2. COMPILE │ ──▶ │ 3. RUNTIME │
│ │ │ │ │ │
│ You write │ │ You run │ │ You execute │
│ code in IDE │ │ compiler │ │ the binary │
│ │ │ explicitly │ │ │
└──────────────┘ └──────────────┘ └──────────────┘
(Just typing) (Build step) (Program runs)Modern IDEs blur the line:
| What You See | What's Actually Happening |
|---|---|
| Red squiggly lines while typing | IDE secretly runs compiler in background |
| "Problems" panel auto-updating | Language server doing mini-compile checks |
| Auto-complete suggestions | IDE parsing your code continuously |
This feels like "real-time compilation," but it's just your IDE being helpful. The actual compile time is still the explicit build step.
Ask yourself: Does the compiler produce a runnable program?
| Scenario | Phase |
|---|---|
| Typing code, seeing red underlines | Editing (+ IDE background checks) |
Running cargo build, getting errors | Compile time |
Double-clicking .exe, program crashes | Runtime |
| Phase | Essay Writing Analogy |
|---|---|
| Editing | Writing the essay in Word |
| Compile time | Grammar/spell check + printing to PDF |
| Runtime | Someone actually reads the printed essay |
So: writing ≠ compiling. Compile time is the deliberate step where your code gets turned into an executable.
An interpreter is a program that executes your code line-by-line without compiling it first.
Your Source Code
↓
┌─────────────┐
│ INTERPRETER │ ← Reads one line, executes it, then moves to next
└─────────────┘
↓
Output# hello.py
print("Hello")
x = 10
print(x)When you run:
$ python hello.pyThe Python interpreter:
print("Hello") → executes it → prints "Hello"x = 10 → executes it → stores 10 in memoryprint(x) → executes it → prints "10"All happens at runtime, no compilation step.
| Aspect | Interpreter (Python, JS) | Compiler (C, Rust) |
|---|---|---|
| Process | Read & execute line-by-line | Compile once, then run binary |
| Speed | Slower (translation every run) | Faster (no translation at runtime) |
| Output | No binary file | Produces .exe / binary |
| Error detection | Runtime only | Compile time (mostly) |
| Distribution | Need interpreter installed | Just need the binary |
Interpreters run on your machine as a program itself:
Your Computer
├─ Operating System (macOS/Windows/Linux)
│ └─ Python Interpreter (you installed this)
│ └─ Your Python script runs INSIDE this
│
└─ Node.js Interpreter (you installed this)
└─ Your JavaScript runs INSIDE thisBefore running interpreted code, you must install the interpreter:
# Install Python interpreter
$ brew install python
# Install Node.js interpreter
$ brew install node
# Now you can run scripts
$ python script.py
$ node script.js┌─────────────────────────────────────────────────┐
│ YOUR COMPUTER │
├─────────────────────────────────────────────────┤
│ Operating System (macOS) │
│ ├─ Applications │
│ │ ├─ Python 3.11 (INTERPRETER) │
│ │ │ └─ Reads & runs .py files │
│ │ │ │
│ │ └─ Node.js 20 (INTERPRETER) │
│ │ └─ Reads & runs .js files │
│ │ │
│ └─ Compiled Programs │
│ ├─ Safari (compiled binary) │
│ └─ VS Code (compiled binary) │
└─────────────────────────────────────────────────┘JavaScript in Browser:
Your Browser has a JS Interpreter (V8 for Chrome, SpiderMonkey for Firefox)
When you visit a website:
1. Browser downloads .js file
2. Built-in interpreter reads it
3. Executes line-by-line
4. Website becomes interactive
No compilation needed - browser has interpreter built-inInterpreter = A program that runs your other programs
V8 is Google's JavaScript engine. It's the interpreter + JIT compiler that runs JavaScript in Chrome and Node.js.
Your JavaScript Code
↓
┌─────────────────┐
│ V8 ENGINE │
├─────────────────┤
│ 1. Parser │ → Reads your JS code
│ 2. Interpreter │ → Executes it line-by-line (slow)
│ 3. JIT Compiler │ → Compiles hot code to machine code (fast)
│ 4. Garbage │ → Cleans up unused memory
│ Collector │
└─────────────────┘
↓
Executed CodeV8 is a hybrid — it's both interpreter AND compiler:
Phase 1: Interpreter (Ignition)
function add(a, b) {
return a + b;
}
add(1, 2); // V8 interprets this line-by-line
add(3, 4); // Still interpretingPhase 2: JIT Compiler (TurboFan)
add(1, 2); // V8: "This function runs a lot!"
add(3, 4); // V8: "I'll compile it to machine code for speed"
// Now add() runs as fast as C code!JIT = Just-In-Time compilation. It compiles code while running based on usage patterns.
┌─────────────────────────────────────────────┐
│ CHROME BROWSER │
├─────────────────────────────────────────────┤
│ ┌───────────────────────────────────────┐ │
│ │ V8 Engine (built into Chrome) │ │
│ │ ┌─────────────────────────────────┐ │ │
│ │ │ Your JavaScript runs here │ │ │
│ │ │ (web pages, React, etc.) │ │ │
│ │ └─────────────────────────────────┘ │ │
│ └───────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────┐ │
│ │ Blink (renders HTML/CSS) │ │
│ └───────────────────────────────────────┘ │
└─────────────────────────────────────────────┘┌─────────────────────────────────────────────┐
│ NODE.JS (Server) │
├─────────────────────────────────────────────┤
│ ┌───────────────────────────────────────┐ │
│ │ V8 Engine (built into Node.js) │ │
│ │ ┌─────────────────────────────────┐ │ │
│ │ │ Your JavaScript runs here │ │ │
│ │ │ (Express, server code, etc.) │ │ │
│ │ └─────────────────────────────────┘ │
│ └───────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────┐ │
│ │ libuv (handles I/O, filesystem) │ │
│ └───────────────────────────────────────┘ │
└─────────────────────────────────────────────┘| Feature | Simple Interpreter | V8 Engine |
|---|---|---|
| Executes JS | Yes | Yes |
| Compiles to machine code | No | Yes (JIT) |
| Fast execution | No | Yes (after JIT) |
| Garbage collection | Yes | Yes |
| Optimizations | No | Yes (inline, etc.) |
Old days (2008): JavaScript was slow (pure interpretation)
Today (V8): JavaScript is almost as fast as compiled languages because:
V8 = JavaScript engine that:
Without V8, JavaScript would be slow like old Python. With V8, it's fast enough to run Gmail, Facebook, and VS Code.
JIT (Just-In-Time) compilation turns the most repeated code into binary machine code while the program is running.
JIT (Just-In-Time) = Compiles frequently-used code to machine code while the program is running
Example:
function calculate() {
return Math.sqrt(100 * 50);
}
// First few runs:
calculate(); // V8 interprets (slow)
calculate(); // V8 interprets (slow)
calculate(); // V8 interprets (slow)
calculate(); // V8: "This is hot code!" → Compiles to machine code
// Now it's compiled:
calculate(); // Runs as fast as C (machine code)
calculate(); // Runs as fast as CThe key word is "repeated" — V8 watches which functions run often and optimizes those specifically. Code that runs once stays interpreted.
| Type | When Compilation Happens | Example |
|---|---|---|
| AOT | Before running | C, Rust, Go |
| JIT | While running | V8 (JavaScript) |
| Bytecode | Before running, to intermediate | Python, Java |
Your Python Code
↓
┌─────────────────┐
│ CPython Engine │
├─────────────────┤
│ 1. Lexer/Parser │ → Breaks code into tokens, builds AST
│ 2. Compiler │ → Compiles to BYTECODE (.pyc files)
│ 3. Interpreter │ → Executes bytecode on Python VM
│ 4. Garbage │ → Reference counting GC
│ Collector │
│ 5. Memory │ → Object allocator
│ Manager │
└─────────────────┘Key difference from V8: Python compiles to bytecode (not machine code), then interprets that bytecode.
# script.py
def add(a, b):
return a + b
print(add(1, 2))When you run python script.py:
__pycache__/script.cpython-311.pycThe bytecode looks like this:
# Disassembly of script.py
1 0 RESUME 0
2 2 LOAD_FAST 0 (a)
4 LOAD_FAST 1 (b)
6 BINARY_OP 0 (+)
10 RETURN_VALUE| Component | V8 (JavaScript) | Python (CPython) |
|---|---|---|
| Parser | Yes | Yes |
| Compiler | Yes (to machine code) | Yes (to bytecode) |
| Interpreter | Yes | Yes |
| JIT Compiler | Yes (TurboFan) | No |
| Garbage Collector | Yes | Yes |
| Virtual Machine | No (direct CPU) | Yes (Python VM) |
┌─────────────────────────────────────────────────────────────────┐
│ UNIVERSAL ENGINE PIPELINE │
└─────────────────────────────────────────────────────────────────┘
Source Code
↓
[LEXER / TOKENIZER] → Breaks text into tokens
↓
[PARSER] → Builds Abstract Syntax Tree (AST)
↓
[SEMANTIC ANALYSIS] → Checks types, scope, rules
↓
[CODE GENERATION] → Converts to executable form
↓
[EXECUTION] → Runs the code
↓
[MEMORY MANAGEMENT] → Cleans upInput: Raw text
def add(a, b):
return a + bOutput: Tokens
[DEF, IDENTIFIER(add), LPAREN, IDENTIFIER(a), COMMA, IDENTIFIER(b), RPAREN,
COLON, RETURN, IDENTIFIER(a), PLUS, IDENTIFIER(b)]Input: Tokens Output: Abstract Syntax Tree (AST)
FunctionDef
/ | \
def add Body
/ \
Return BinaryOp
| / \
Identifier Identifier +
a a bWhat it checks:
x = 10
y = x + "hello" # Error: Can't add int + stringThis is where engines diverge:
| Engine | Generates | Then Executes Via |
|---|---|---|
| CPython | Bytecode | Python VM |
| V8 | Machine code (for hot code) | Direct CPU |
| Rust | Machine code | Direct CPU |
| Java | Bytecode | JVM |
| Go | Machine code | Direct CPU |
Bytecode engines (Python, Java):
Bytecode → Virtual Machine → CPUNative engines (C, Rust, V8-compiled):
Machine Code → CPU (direct)Two approaches:
Automatic (GC):
Manual:
┌─────────────────────────────────────────────────────────────────┐
│ YOUR CODE │
│ def add(a, b): return a + b │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ LEXER (Tokenizing) │
│ [def, add, (, a, ,, b, ), :, return, a, +, b] │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ PARSER (AST) │
│ ┌─────────────┐ │
│ │ FunctionDef │ │
│ └─────────────┘ │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ SEMANTIC ANALYSIS │
│ ✓ Variables defined? ✓ Types match? │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ CODE GENERATION │
│ ↓↓↓ ENGINE DECISION POINT ↓↓↓ │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Bytecode │ │ Machine Code │ │ Machine Code │ │
│ │ (Python) │ │ (V8, Rust) │ │ (C, Go) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ EXECUTION │
│ ↓↓↓ HOW IT RUNS ↓↓↓ │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ VM → CPU │ │ Direct CPU │ │ Direct CPU │ │
│ │ (Python) │ │ (V8, Rust) │ │ (C, Go) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ MEMORY MANAGEMENT │
│ ↓↓↓ CLEANUP ↓↓↓ │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ GC │ │ GC │ │ Ownership │ │
│ │ (Python) │ │ (V8, JS) │ │ (Rust) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────────┘
↓
PROGRAM OUTPUTAll engines follow the same flow. The only difference is:
Step 4 (Code Generation) + Step 5 (Execution) + Step 6 (Memory Management)
That's where each engine makes its design choice:
Everything else (lexing, parsing, semantic analysis) is essentially the same across all languages.
| Language | Engine Name | What We Call It |
|---|---|---|
| Python | CPython | Python interpreter / Python engine |
| JavaScript (Chrome/Node) | V8 | JavaScript engine |
| JavaScript (Safari) | JavaScriptCore | JavaScript engine |
| JavaScript (Firefox) | SpiderMonkey | JavaScript engine |
| Java | HotSpot JVM | Java Virtual Machine |
| Ruby | CRuby | Ruby interpreter |
| Go | gc | Go compiler |
Note: The terms "engine," "interpreter," "runtime," and "VM" are sometimes used interchangeably, but they all mean the same thing: the software that makes your code run.
Every language needs one piece of software that:
That software is the engine/interpreter/runtime.
For Python → CPython For JavaScript (Chrome) → V8 For JavaScript (Safari) → JavaScriptCore For Java → JVM
| Phase | What Happens | When |
|---|---|---|
| Compile Time | Code is checked, converted to executable | When you run build command |
| Runtime | Program actually executes | When you run the binary |
| Type | Process | Example |
|---|---|---|
| Compiled | Source → Compiler → Binary → CPU | C, Rust, Go |
| Interpreted | Source → Interpreter → CPU | Python, JavaScript |
| Approach | Language | Pros/Cons |
|---|---|---|
| GC | Python, JS, Java | Automatic but pauses performance |
| Manual | C, C++ | Fast but error-prone |
| Ownership | Rust | Safe and fast, compile-time checked |
| Component | Purpose | Present In |
|---|---|---|
| Lexer | Breaks code into tokens | All engines |
| Parser | Builds AST | All engines |
| Semantic Analysis | Type/scope checking | All engines |
| Code Generator | Converts to executable form | All engines |
| JIT Compiler | Optimizes hot code | V8, some others |
| GC | Automatic memory cleanup | Python, V8, Java |
| VM | Executes bytecode | Python, Java |
This guide covers the fundamental concepts of how programming languages work under the hood. Understanding these concepts will help you:
The key takeaway: All languages follow the same basic pipeline, but differ in their specific design choices at the code generation and execution stages. These choices determine the language's performance, safety, and ease of use.