Compiler DesignUnit 111 min read
Compiler Design: Phases, Roles & Real-World Impact
Unit 1 of Compiler Design introduces the fundamental concepts of compilers—what they are, why they exist, their architecture, and the phases they undergo to translate high-level code into machine code, with real-world applications in Nepalese software and systems.
TAKEAWAYS:
- A compiler is a program that translates high-level code (e.g., C, Java) into machine code while preserving semantics, unlike interpreters that execute line-by-line.
- Compilers follow multi-phase processing: lexical → syntax → semantic → intermediate → optimization → code generation, each with distinct tasks and data structures.
- Lexical analysis converts source code into tokens (e.g.,
if,+,5), while syntax analysis builds parse trees to validate grammar rules. - Syntax-directed translation attaches semantic actions (e.g., type checking, symbol table updates) to grammar rules, enabling intermediate code generation.
- Real-world compilers (e.g., GCC for Linux, Java’s
javac) optimize performance, reduce errors, and enable cross-platform execution (e.g., Python →.exevia PyInstaller). - Exam focus: Define phases, compare compilers vs. interpreters, and explain how a simple statement (e.g.,
x = a + b) flows through phases.
What Is a Compiler?
A compiler is a system software that converts high-level programming language (HLL) code (e.g., C, Python) into machine code (binary) or low-level assembly for execution. Unlike interpreters (e.g., Python’s CPython), compilers produce an executable file upfront, improving speed and portability.
Why Use Compilers?
| Feature | Compiler | Interpreter |
|---|---|---|
| Execution Speed | Faster (pre-compiled to machine code) | Slower (executes line-by-line) |
| Portability | Limited (target-specific binary) | High (runs on any system with interpreter) |
| Error Detection | Early (all errors reported at once) | Late (errors appear during execution) |
| Memory Usage | Lower (optimized binary) | Higher (interpreter overhead) |
Example in Nepal:
- eSewa’s backend uses compiled languages (Java, C++) for high-speed transaction processing.
- Khalti’s mobile app relies on compiled native code (Kotlin/Java) for performance-critical tasks like payment validation.
Phases of a Compiler
Compilers process source code in 9 phases, grouped into front-end (language-specific) and back-end (machine-specific) tasks. Below is the pipeline with key data structures:
flowchart TD
A["Source Code"] --> B["Lexical Analyzer"]
B --> C["Tokens"]
C --> D["Syntax Analyzer"]
D --> E["Parse Tree"]
E --> F["Semantic Analyzer"]
F --> G["Intermediate Code"]
G --> H["Optimizer"]
H --> I["Target Code Generator"]
I --> J["Machine Code"]1. Lexical Analysis (Scanner)
Goal: Convert source code into tokens (lexemes + categories). How it works:
- Reads characters → groups into tokens (e.g.,
if,+,5). - Uses finite automata (DFA/NFA) to recognize patterns (keywords, identifiers, operators).
- Ignores whitespace/comments.
Example:
Input: int x = 5 + 3;
Output Tokens:
| Token Type | Lexeme |
|---|---|
KEYWORD |
int |
IDENTIFIER |
x |
OPERATOR |
= |
INTEGER |
5 |
OPERATOR |
+ |
INTEGER |
3 |
PUNCTUATION |
; |
Visual: Finite Automaton for Identifiers
Real-World Link:
- GCC’s
cpppreprocessor performs lexical analysis before compilation. - Pathao’s ride-matching system uses tokenization to parse user inputs (e.g.,
pickup: "Kathmandu"→TOKEN: LOCATION).
2. Syntax Analysis (Parser)
Goal: Validate grammar and build a parse tree or abstract syntax tree (AST). How it works:
- Uses grammar rules (e.g., BNF) to check if tokens form valid sentences.
- Two main methods:
- Top-down parsing (LL parsers, e.g., recursive descent).
- Bottom-up parsing (LR parsers, e.g., shift-reduce).
Example Grammar (Arithmetic Expressions):
E → T E'
E' → + T E' | ε
T → F T'
T' → * F T' | ε
F → ( E ) | id
Input: a + b * c
Parse Tree:
Real-World Link:
- Ncell’s billing system uses parsers to validate user commands (e.g.,
balance check). - Daraz’s order processing parses customer inputs (e.g.,
add item: "iPhone 15"→ validates syntax before processing).
3. Semantic Analysis
Goal: Attach meaning to the parse tree (e.g., type checking, scope resolution). Key Tasks:
- Symbol table management: Tracks variables/functions (name, type, scope).
- Type checking: Ensures operations are valid (e.g.,
int + string→ error). - Scope rules: Resolves variable declarations (global vs. local).
Example:
int x = 5;
x = x + "hello"; // Error: Type mismatch (int + string)
Symbol Table:
| Variable | Type | Scope |
|---|---|---|
x |
int |
Global |
y |
float |
Local |
Visual: Symbol Table Update
Real-World Link:
- Nepal Rastra Bank’s loan calculator performs semantic checks to ensure inputs (e.g.,
principal,interest_rate) are numeric and valid. - YouTube’s comment system uses semantic analysis to flag invalid inputs (e.g., SQL injection attempts).
4. Intermediate Code Generation
Goal: Produce portable intermediate code (e.g., three-address code, quadruples) for optimization.
Example (Three-Address Code):
Source: z = a + b * c
Intermediate Code:
t1 = b * c
z = a + t1
Quadruple Representation:
| Op | Arg1 | Arg2 | Result |
|---|---|---|---|
* |
b |
c |
t1 |
+ |
a |
t1 |
z |
Real-World Link:
- Google’s V8 JavaScript engine generates intermediate bytecode before JIT compilation.
- NTC’s network traffic analyzer uses intermediate representations to detect anomalies in packet headers.
5. Code Optimization
Goal: Improve performance/size of intermediate code. Techniques:
- Constant folding:
x = 2 + 3→x = 5. - Dead code elimination: Remove unreachable code.
- Loop optimization: Unroll loops or use strength reduction.
Example: Before:
t1 = 2 + 3
x = t1
After (Constant Folding):
x = 5
Real-World Link:
- WhatsApp’s end-to-end encryption uses optimization to reduce battery drain on mobile devices.
- NEPSE’s stock trading platform optimizes queries to handle high-frequency trades efficiently.
6. Code Generation
Goal: Convert optimized intermediate code to target machine code (assembly or binary). Steps:
- Instruction selection: Map intermediate ops to machine instructions.
- Register allocation: Assign variables to CPU registers.
- Assembly generation: Produce assembly code (e.g., x86, ARM).
Example (x86 Assembly):
Source: int sum = a + b;
Assembly:
MOV EAX, [a] ; Load 'a' into EAX
ADD EAX, [b] ; Add 'b' to EAX
MOV [sum], EAX ; Store result in 'sum'
Real-World Link:
- Daraz’s checkout system generates optimized assembly for fast payment processing.
- Ncell’s SMS gateway compiles SMS parsing logic into efficient machine code.
Compilers vs. Interpreters
| Feature | Compiler | Interpreter |
|---|---|---|
| Execution | Entire program → machine code | Line-by-line execution |
| Speed | Faster (pre-compiled) | Slower (runtime overhead) |
| Portability | Limited (target-specific binary) | High (runs on any system) |
| Error Handling | All errors at once | Errors during execution |
| Examples | GCC, javac, Rust |
Python, JavaScript (Node.js) |
Hybrid Approach:
- Java: Compiled to bytecode → interpreted by JVM (or JIT-compiled).
- C#: Compiled to IL → executed by CLR.
In the Real World
eSewa (Nepal):
- Uses GCC (C/C++) to compile backend services for high-speed transaction processing.
- Lexical/syntax analysis validates user inputs (e.g.,
payment: "Rs. 500"). - Optimization reduces latency during peak hours (e.g., Dashain).
Khalti’s Mobile App:
- Kotlin/Java compiler generates efficient bytecode for Android.
- Symbol tables track user accounts and transaction histories.
- Intermediate code enables cross-platform support (iOS/Android).
Daraz’s Order Fulfillment:
- Parser validates order syntax (e.g.,
add item: "iPhone 15, quantity: 2"). - Code generation optimizes warehouse robot paths (e.g., shortest route to pick items).
- Parser validates order syntax (e.g.,
NTC’s Network Monitoring:
- Lexical analyzer scans packet headers for anomalies.
- Semantic checks ensure protocol compliance (e.g., TCP handshake).
Nepal Rastra Bank’s Loan System:
- Compiler optimizations reduce processing time for loan approvals.
- Symbol tables track borrower details (e.g.,
loan_id,interest_rate).
Worked Example: Compiling x = a + b
Let’s trace the compilation of this statement through all phases.
Phase 1: Lexical Analysis
Input: x = a + b;
Tokens:
| Type | Lexeme |
|---|---|
IDENTIFIER |
x |
OPERATOR |
= |
IDENTIFIER |
a |
OPERATOR |
+ |
IDENTIFIER |
b |
PUNCTUATION |
; |
Phase 2: Syntax Analysis
Grammar:
S → ID = E
E → E + T | T
T → ID
Parse Tree:
Phase 3: Semantic Analysis
Symbol Table:
| Variable | Type | Scope |
|---|---|---|
a |
int |
Global |
b |
int |
Global |
x |
int |
Global |
Phase 4: Intermediate Code (Three-Address)
t1 = a
t2 = b
t3 = t1 + t2
x = t3
Phase 5: Optimization
After constant folding (if a and b are constants):
x = 5 + 3 → x = 8
Phase 6: Code Generation (x86 Assembly)
MOV EAX, [a] ; Load 'a'
ADD EAX, [b] ; Add 'b'
MOV [x], EAX ; Store in 'x'
Exam Tip
Define Key Terms:
- Compiler: "A program that translates HLL to machine code."
- Lexeme: "A sequence of characters in source code (e.g.,
if)." - Parse Tree: "A tree representing the syntactic structure of code."
Phase Order: Memorize the 9 phases in sequence: Lexical → Syntax → Semantic → Intermediate → Optimization → Code Generation.
Diagrams Are Critical:
- Draw parse trees for syntax questions.
- Show symbol table updates for semantic analysis.
- Illustrate finite automata for lexical analysis.
Real-World Applications:
- Link compilers to Nepali systems (eSewa, Khalti, NTC).
- Explain how optimization reduces latency in Daraz/Ncell.
Common Pitfalls:
- Confusing interpreters (execute line-by-line) with compilers (full translation).
- Forgetting semantic analysis (type checking, symbol tables).
- Skipping optimization in intermediate code questions.
Practice Questions:
- Given a grammar, build a parse tree for
a + b * c. - Trace lexical analysis for
while (x > 0) { x--; }. - Optimize the intermediate code:
t1 = 2 * 3; y = t1 + 1.
- Given a grammar, build a parse tree for
Visual Summary:
mindmap
root((Compiler Phases))
Lexical Analysis
Tokens
Finite Automata
Syntax Analysis
Parse Trees
Grammar Rules
Semantic Analysis
Symbol Tables
Type Checking
Intermediate Code
Three-Address Code
Quadruples
Optimization
Constant Folding
Dead Code Elimination
Code Generation
Assembly
Machine CodeBased on the PU BE Computer (PU) syllabus for Compiler Design (CMP360), unit 1.
Discussion
Loading…