CD Notes
Condensed study notes for quick exam preparation and revision
How to Use These Notes
These revision notes are designed for the night before your exam or during last-minute preparation. They condense the entire compiler design course into key facts, formulas, and decision rules that frequently appear in exam questions. Read through them once to refresh your memory, then focus on areas where you feel least confident. Each section is independent — jump to whatever topic you need.
The Compilation Pipeline (Must Memorize This Order)
The complete journey from source code to execution follows these phases, and exam questions frequently ask you to identify which phase detects a specific error or performs a specific transformation:
| 1. Preprocessing | Macro expansion, file inclusion (#include, #define) |
| 2. Lexical Analysis | Characters → Tokens (removes whitespace/comments) |
| 3. Syntax Analysis | Tokens → Parse Tree (checks grammar rules) |
| 4. Semantic Analysis | Parse Tree → Annotated Tree (type/scope checking) |
| 5. IR Generation | Annotated Tree → Three-Address Code |
| 6. Optimization | Improve IR (constant folding, dead code removal) |
| 7. Code Generation | IR → Assembly/Machine Code |
| 8. Assembly | Assembly → Object Code (.o files) |
| 9. Linking | Object files → Executable (resolves external references) |
| 10. Loading | Executable → Running process in memory |
Key exam question pattern: "At which phase is error X detected?"
int x = ;→ Syntax analysis (invalid grammar)int x = "hello";→ Semantic analysis (type mismatch)printf("%d", x)where x is undeclared → Semantic analysis- Division by zero with variable → Runtime (cannot detect at compile time)
#include "missing.h"→ Preprocessing
Parsing Methods: When to Use Which
LL(1) Parsing
- Direction: Top-down (starts from start symbol, builds tree downward)
- Scan: Left-to-right
- Derivation: Leftmost
- Lookahead: 1 token
- Cannot handle: Left-recursive grammars (infinite loop)
- Requires: Left recursion elimination + left factorization
- Uses: FIRST and FOLLOW sets to build parsing table
- Implementation: Table-driven or recursive descent
LR/SLR/LALR/CLR Parsing
- Direction: Bottom-up (starts from input, builds tree upward)
- Scan: Left-to-right
- Derivation: Rightmost (in reverse)
- Power hierarchy: LR(1) > LALR(1) > SLR(1) > LR(0)
- Can handle: Left-recursive grammars (no problem)
- Uses: Shift-reduce actions with item sets
- Implementation: Table-driven (ACTION and GOTO tables)
Exam tip: If asked "which parser is most powerful?", the answer is CLR(1)/LR(1). If asked "which is most practical?", the answer is LALR(1) (used by Bison).
FIRST and FOLLOW Set Calculation (Critical for Exams)
Rules for FIRST(X):
- If X is a terminal: FIRST(X) = {X}
- If X → ε is a production: add ε to FIRST(X)
- If X → Y₁Y₂...Yₖ: add FIRST(Y₁) - {ε} to FIRST(X). If ε ∈ FIRST(Y₁), also add FIRST(Y₂) - {ε}. Continue until you find Yᵢ where ε ∉ FIRST(Yᵢ). If all Y's can derive ε, add ε.
Rules for FOLLOW(A):
- If A is start symbol: add $ to FOLLOW(A)
- If B → αAβ: add FIRST(β) - {ε} to FOLLOW(A)
- If B → αA, or B → αAβ where ε ∈ FIRST(β): add FOLLOW(B) to FOLLOW(A)
Worked Example:
Grammar: E → TE', E' → +TE' | ε, T → FT', T' → *FT' | ε, F → (E) | id
FIRST(F) = {(, id}
FIRST(T') = {*, ε}
FIRST(T) = FIRST(F) = {(, id}
FIRST(E') = {+, ε}
FIRST(E) = FIRST(T) = {(, id}
FOLLOW(E) = {$, )}
FOLLOW(E') = FOLLOW(E) = {$, )}
FOLLOW(T) = FIRST(E')-{ε} ∪ FOLLOW(E) = {+, $, )}
FOLLOW(T') = FOLLOW(T) = {+, $, )}
FOLLOW(F) = FIRST(T')-{ε} ∪ FOLLOW(T) = {*, +, $, )}
Grammar Transformations (Frequently Tested)
Left Recursion Elimination:
| Original: A | Aα | β |
| Transformed: A | βA' |
| A' | αA' | ε |
Example: E → E + T | T becomes E → TE', E' → +TE' | ε
Left Factorization:
| Original: A | αβ | αγ |
| Factored: A | αA' |
| A' | β | γ |
Example: S → iCtSa | iCtSeS | a becomes S → iCtSS' | a, S' → a | eS
Type System Quick Reference
| char | short → int → long → float → double |
| string + int | ERROR |
| array = scalar | ERROR (unless element assignment) |
| void + anything | ERROR |
Optimization Techniques Summary
| Technique | What It Does | Example |
|---|---|---|
| Constant Folding | Evaluate constants at compile time | x = 2+3 → x = 5 |
| Constant Propagation | Replace variable with known value | After x=5: y=x+1 → y=6 |
| Dead Code Elimination | Remove unreachable/unused code | if(false){...} → removed |
| Common Subexpression | Reuse previously computed values | a+b computed twice → compute once |
| Loop Invariant Motion | Move constant computation outside loop | x=a*b inside loop → move before loop |
| Strength Reduction | Replace expensive ops with cheap ones | x*2 → x<<1 |
| Peephole Optimization | Improve small code sequences | MOV R1,R2; MOV R2,R1 → MOV R1,R2 |
Symbol Table Operations
Implementation options (by speed)
Hash table: O(1) average lookup — BEST CHOICE
BST: O(log n) lookup
Linear list: O(n) lookup — only for tiny programs
Scope management
Enter scope → Push new symbol table onto scope stack
Exit scope → Pop top table from scope stack
Lookup → Search from top of stack downward (finds innermost declaration)
Three-Address Code Generation
For expression a = b * c + d:
For if (x < y) a = 1; else a = 2;:
Common Exam Mistakes to Avoid
- Forgetting ε in FIRST sets — if a nonterminal can derive empty, ε must be in its FIRST set
- Not propagating FOLLOW correctly — if A → αB and ε ∈ FIRST of everything after B, then FOLLOW(B) includes FOLLOW(A)
- Confusing synthesis and inheritance — synthesized attributes flow UP the tree, inherited attributes flow DOWN
- Wrong conflict type — shift-reduce means parser unsure whether to shift or reduce; reduce-reduce means unsure which rule to reduce by
- Forgetting augmented grammar — LR parsers add S' → S to detect acceptance
- Type promotion direction — always narrow → wide (int → float), never implicit narrowing
Problem-Solving Strategies for Exams
For "construct parsing table" questions: Calculate FIRST and FOLLOW first. Then for each production A → α, put it in table[A, a] for each a in FIRST(α). If ε ∈ FIRST(α), also put it in table[A, b] for each b in FOLLOW(A).
For "trace the parser" questions: Show the stack, input, and action at each step. For LR parsing: shift pushes state+token, reduce pops RHS and pushes LHS with GOTO state.
For "generate three-address code" questions: Break complex expressions into simple operations with temporaries. Each instruction has at most one operator on the right side.
For "identify the optimization" questions: Look for patterns — constant expressions (folding), repeated computations (CSE), variables assigned but unused (dead code), computations inside loops that don't change (loop invariant).
Key Formulas to Memorize
| Array address | base + (i - lower) × element_size |
| 2D array (row-major) | base + ((i - lr) × cols + (j - lc)) × size |
| Stack frame size | Σ(local sizes) + return_addr + saved_regs + alignment |
| DFA states | at most 2^n states from NFA with n states |
| LL(1) condition: For A | α|β, FIRST(α) ∩ FIRST(β) = ∅ |
Exam Focus
Revise definitions, diagrams, examples, and short-answer points for Quick Revision Notes for Exams.
Interview Use
Prepare one clear explanation, one practical example, and one common mistake for this Compiler Design topic.
Search Terms
compiler-design, compiler design, compiler, design, resources, quick, revision, notes
Related Compiler Design Topics