UNIT 1: COMPILER DESIGN - EXAM-FOCUSED SHORT NOTES
1.0 INTRODUCTION TO COMPILERS & OVERVIEW
Definition: A compiler is a program that translates source code written in a high-level language into an equivalent target language (usually machine code or assembly).
Phases of a Compiler (with Primary Tasks):
-
Lexical Analysis (Scanner): Reads input stream, groups characters into tokens (identifiers, keywords, operators), removes whitespace/comments.
-
Syntax Analysis (Parser): Checks if token stream follows the language's grammar rules (CFG). Builds a parse tree or abstract syntax tree (AST).
-
Semantic Analysis: Checks for semantic consistency (type checking, scope rules). Uses a symbol table. Generates intermediate code annotations.
-
Intermediate Code Generation: Produces a platform-independent, low-level representation (e.g., Three-Address Code (TAC)).
-
Code Optimization: Improves intermediate code for efficiency (speed, size). Can be local or global.
-
Code Generation: Maps optimized intermediate code to target machine code. Manages storage allocation (registers, memory).
-
Symbol Table Management: Centralized data structure storing information about identifiers (name, type, scope, address).
-
Error Handling: Detects, reports, and recovers from errors at various phases.
[!TIP] Exam Focus: Be prepared to list phases in order and state the key output of each. Questions often ask for "phase-wise output" for a given expression.
Compiler Construction Tools:
-
Lex/Flex: Generates lexical analyzers from regular expressions.
-
Yacc/Bison: Generates parsers (LALR(1)) from context-free grammars.
-
JavaCC, ANTLR: Modern, powerful parser generators for Java/other languages.
Passes & Bootstrapping:
-
Single-pass Compiler: Combines analysis and synthesis in one pass. Simple, but limited optimization. Used for scripting languages.
-
Multi-pass Compiler: Separate phases (passes). Enables complex optimization and better error recovery. Most production compilers.
-
Bootstrapping: Technique of writing a compiler in the language it compiles. Involves stages:
-
Write simple compiler (C0) for a subset of language L in another language.
-
Use C0 to compile a more advanced compiler C1 (written in L) for full L.
-
Now C1 can compile itself and other L programs.
-
2.0 LEXICAL ANALYSIS (SCANNER)
Role & Function:
-
Input Buffering: Uses two buffers (
lexemeBegin,forward) with a sentinel character to efficiently read ahead without system calls.[ Buffer 1 ] [ Buffer 2 ] ^ ^ lexemeBegin forward -
Token Recognition: Matches patterns defined by regular expressions. Keywords are recognized separately from identifiers (often via a hash table lookup).
Regular Expressions (RE) & Finite Automata (FA):
-
RE to NFA: Using Thompson's construction (ε-transitions).
-
NFA to DFA: Subset construction method.
-
DFA Minimization: Merge equivalent states (using partitioning algorithm).
-
Pattern Matching Example: Language of strings with odd number of
aand odd number ofb:
$$L = \{ w \in \{a,b\}^* \mid \#_a(w) \text{ odd} \land \#_b(w) \text{ odd} \}$$
**RE:** `(b(a|b)*a(a|b)*b(a|b)*)|(a(a|b)*b(a|b)*a(a|b)*)`
Lex/Flex Program Structure:
%{
/* C declarations (token definitions, includes) */
%}
/* Regular Expression Definitions */
DIGIT [0-9]
ID [a-zA-Z_][a-zA-Z0-9_]*
%%
{DIGIT}+ { yylval.num = atoi(yytext); return NUM; }
{ID} { if (isKeyword(yytext)) return KEYWORD; else return ID; }
"+"|"-"|"*"|"/" { return yytext[0]; }
. { /* Handle illegal character */ }
%%
/* User-defined functions (e.g., isKeyword) */
[!TIP] Common Pitfall: Keywords must be checked before identifiers in the rules section, or they will be tokenized as
ID.
Lexical Errors & Handling:
-
Error Types: Illegal character, unterminated comment/string, token too long.
-
Recovery Strategies:
-
Panic Mode: Discard characters until a valid delimiter (whitespace, semicolon) is found.
-
Deletion: Delete a problematic character and continue.
-
Insertion: Insert a missing character (e.g., quote) and continue.
-
Key Distinction: Identifier vs. Keyword
-
Identifier: User-defined name (variable, function). Pattern:
[a-zA-Z_][a-zA-Z0-9_]*. Recognized by matching the general pattern, then checking against a keyword table. -
Keyword: Reserved word with fixed meaning (
if,while,int). Recognized by having explicit rules before the identifier rule in Lex, or via a symbol table lookup that returns a specific token type.
3.0 SYNTAX ANALYSIS (PARSING)
Role & CFG:
-
CFG:
G = (V, T, P, S)whereV= variables (non-terminals),T= terminals (tokens),P= productions,S= start symbol. -
BNF Example:
<stmt> → if <expr> then <stmt> else <stmt> | while <expr> do <stmt> | { <stmt-list> } <expr> → <term> + <expr> | <term> <term> → <factor> * <term> | <factor> -
Parse Tree: Shows hierarchical structure. Derivation: Leftmost/Rightmost.
-
Ambiguity: Grammar allows multiple parse trees for same string. Resolved by rewriting grammar or using parser with precedence (e.g., Yacc
%left,%right).
Parsing Techniques:
| Technique | Approach | Examples | Pros | Cons |
|---|---|---|---|---|
| Top-Down | Start from S, derive input string. |
Recursive Descent, Predictive (LL(1)) | Simple to implement by hand. | Cannot handle left-recursion; limited lookahead. |
| Bottom-Up | Start from input, reduce to S. |
Shift-Reduce, LR(k) (LR(0), SLR(1), LALR(1), LR(1)) | Powerful; handles most CFGs; good error detection. | Complex table construction. |
Predictive Parsing (LL(1)):
-
Compute FIRST(α) for each production
A → α. -
Compute FOLLOW(A) for each non-terminal
A. -
Construct Parsing Table
M[A, a]:-
For each
A → αand eacha ∈ FIRST(α), setM[A, a] = A → α. -
If
ε ∈ FIRST(α), for eachb ∈ FOLLOW(A), setM[A, b] = A → α.
-
-
Grammar is LL(1) iff: No entry in
Mhas multiple productions.
LR Parsing (Bottom-Up Powerhouse):
-
Core Idea: Shift tokens onto a stack until a handle (rightmost substring of a production) is found, then reduce.
-
LR(0) Items: Production with a dot
•showing parser's position:A → α•β. -
Canonical Collection: States are sets of LR(0) items. Closure and Goto functions build the state machine.
-
Parsing Table:
ACTION[i, a](shift, reduce, accept, error) andGOTO[i, A](state transition). -
Hierarchy of Power:
LR(1) > LALR(1) > SLR(1) > LR(0)
-
LR(1): Uses 1-symbol lookahead in items. Largest tables, most powerful.
-
LALR(1): Merges LR(1) states with same core (ignoring lookahead). Used by Yacc/Bison. Good balance.
-
SLR(1): Uses FOLLOW(A) to decide reductions. Simpler but less powerful than LALR.
-
[!TIP] Exam Critical: You WILL be asked to construct an SLR or LALR parsing table. Steps:
- Eliminate left-recursion, ensure grammar is suitable.
- Augment grammar:
S' → S.
- Build canonical collection of LR(0) items.
- For SLR: Use FOLLOW sets to fill
reduceactions. For LALR: Merge states with same core first, then use lookaheads from original LR(1) items.
Parser Comparison (SLR vs. LALR vs. LR):
| Feature | SLR(1) | LALR(1) | LR(1) |
|---|---|---|---|
| Lookahead | FOLLOW(A) | Merged lookaheads from LR(1) | Full 1-symbol lookahead in items |
| Table Size | Smallest | Medium | Largest |
| Power | Least (may have conflicts on some grammars) | More (handles most practical grammars) | Most (theoretically optimal) |
| Tool Use | Educational | Yacc/Bison (default) | Rare (large tables) |
Syntactic Errors & Recovery:
-
Detection: When
ACTION[i, a] = error(no valid action). -
Recovery Strategies:
-
Panic Mode: Skip tokens until one in
FOLLOW(current_nonterminal)is found. -
Phrase Level: Insert/delete/replace a single token to synchronize (requires error productions).
-
Error Productions: Add productions like
A → errorto handle common mistakes.
-
4.0 SEMANTIC ANALYSIS
Role & Tasks:
-
Type Checking: Verify operator-operand type compatibility. E.g.,
int + float→ implicit conversion. -
Type Conversion:
-
Implicit (Coercion): Automatically performed (e.g.,
inttofloatin arithmetic). -
Explicit (Casting): Programmer-specified (e.g.,
(float)x).
-
-
Other Checks: Break/continue in loops, return type matching, argument count/type matching.
Symbol Table Management:
-
Purpose: Store and retrieve identifier attributes (name, type, scope, line number, address, size).
-
Data Structures:
-
Linear List (Unordered/Ordered): Simple, slow search (
O(n)). -
Hash Table: Most common. Fast average access (
O(1)). Handles collisions (chaining, open addressing). -
Binary Search Tree (BST):
O(log n)access if balanced. -
Lexical vs. Nested Scopes: For nested procedures, use display (array of pointers to activation records) or static links in activation records for non-local access.
-
Attribute Grammars:
-
Synthesized Attributes: Values computed from children's attributes (bottom-up). E.g., expression value.
E → E1 + T { E.val = E1.val + T.val } -
Inherited Attributes: Values passed from parent/siblings (top-down). E.g., type of identifier in declaration.
D → int X { X.entry.type = int } // Inherited from 'int' -
L-attributed Definitions: A restricted form where inherited attributes can only come from parent and left siblings. Suitable for top-down (LR) translation (e.g., in Yacc semantic actions). Most semantic actions in compilers are L-attributed.
Semantic Errors & Handling:
-
Type Mismatch:
float * string. -
Undeclared Variable: Use before declaration.
-
Multiple Declaration: Same name in same scope.
-
Handling: Report error with line number, identifier, and message. Use symbol table to track scope.
Advanced Semantic Concepts:
-
Function Overloading: Same name, different parameter types. Resolution based on most specific match during compile time.
-
Polymorphic Functions: Functions that work with multiple types (e.g., generics in Java, templates in C++). Type checking is deferred or uses constraints.
5.0 INTERMEDIATE CODE GENERATION
Need & Forms:
-
Need: Machine-independent analysis/optimization before target code generation.
-
Three-Address Code (TAC):
x = y op z(at most 3 operands per instruction). Represents complex expressions as a sequence of simple operations.
Forms of TAC Representation:
| Form | Structure | Pros | Cons |
|---|---|---|---|
| Quadruples | (op, arg1, arg2, result) |
Simple, fixed fields. Easy to generate. | Temporary names (t1, t2) proliferate. |
| Triples | (op, arg1, arg2) |
No extra temporaries; refers to other triples by position. | Hard to optimize (no explicit result pointer). |
| Indirect Triples | (op, arg1, arg2) + list of pointers to triples. |
Allows easy reordering of instructions. | Slightly more complex. |
Example (Expression: a = b * -c + d):
Quadruples:
( *, b, c, t1 )
( -, 0, t1, t2 ) // assuming unary minus as 0 - t1
( +, t2, d, t3 )
( =, t3, -, a )
Triples:
( *, b, c )
( -, 0, (1) )
( +, (2), d )
( =, (3), a )
TAC for Control Flow Statements (Switch):
Switch p+q {
case 1: x=x+1; break;
case 2: y=y+2; break;
case 3: z=z+3; break;
default: c=c-1;
}
TAC:
t1 = p + q
goto L1, L2, L3, L4 based on t1 value // Jump table or if-else chain
L1: x = x + 1
goto L5
L2: y = y + 2
goto L5
L3: z = z + 3
goto L5
L4: c = c - 1
L5:
Backpatching:
-
Purpose: Fill in addresses of jump targets (for
if,while,for) after they are known. -
Mechanism: Maintain lists of pending jumps (unresolved
gotostatements) for each boolean expression. -
Algorithm:
-
For
if (B) S1 else S2: EvaluateB→ generates code with conditional jump toS1and unconditional jump toS2. BackpatchB.truelist toS1.code,B.falselist toS2.code. -
For
while (B) S: Generate labelL1. EvaluateB→B.truejumps toS,B.falsetoL2. AfterS, generategoto L1. BackpatchB.truetoS.code.
-
-
Example:
if (a < b) x = y; else x = z;if a < b goto L1 goto L2 L1: x = y L2: x = zBackpatch lists:
B.true = [next instruction],B.false = [after L1].
Directed Acyclic Graph (DAG):
-
Definition: A DAG for an expression is a graph where:
-
Leaves: Variables/constants (unique).
-
Interior Nodes: Operators. Children: Operands.
-
Key Property: Common subexpressions are shared (same node reused).
-
-
Construction: Process expression tokens left-to-right.
-
For variable/constant: create new leaf node (if not already present).
-
For operator: create new node only if no existing node has same operator and same children (order matters for non-commutative ops).
-
-
Application: Common Subexpression Elimination (CSE). The DAG directly shows which computations can be done once and reused.
-
Example from DEC 2024:
$$E = a + a \times (b - c) + (b - c) \times d$$
**DAG Construction Steps:**
1. `a` → node A.
2. `b` → node B, `c` → node C.
3. `b - c` → node `-`(B,C). Call it N1.
4. `a * N1` → node `*`(A,N1). Call it N2.
5. `a + N2` → node `+`(A,N2). Call it N3.
6. `N1 * d` → node `*`(N1,D) where D is node for `d`. Call it N4.
7. `N3 + N4` → node `+`(N3,N4). **Root.**
**Result:** Nodes `N1` (for `b-c`) is shared. Only 4 unique interior nodes instead of 5.
Translation of Program Constructs (Boolean Expressions):
-
Boolean to TAC: Use relational ops (
<,>,==) that produce 0/1, or generate conditional jumps directly.-
B1 && B2→B1.true→B2;B1.falseandB2.falseare combined. -
B1 || B2→B1.false→B2;B1.trueandB2.trueare combined.
-
6.0 CODE OPTIMIZATION
Need & Classification:
-
Machine-Independent: Applied to intermediate code (TAC, DAG). E.g., constant folding, CSE, dead code elimination.
-
Machine-Dependent: Applied to target code. E.g., register allocation, instruction selection.
-
Local: Within a basic block.
-
Global: Across basic blocks (requires control flow graph).
Fundamental Optimizations:
-
Constant Folding: Evaluate constant expressions at compile time.
x = 3 * 4 + 5→x = 17
-
Constant Propagation: Replace variables with known constant values.
a = 5; b = a + 2;→b = 7
-
Common Subexpression Elimination (CSE): Compute value once, reuse. DAG is key tool.
-
Dead Code Elimination: Remove code whose result is never used (e.g., unreachable statements, assignments to unused variables).
Loop Optimization (Most Impactful):
| Technique | Idea | Example |
|---|---|---|
| Loop-Invariant Code Motion | Move calculations that produce same value in every iteration outside the loop. | for(i=0;i<n;i++) { x = y*z + a[i]; } → t = y*z; for(...) { x = t + a[i]; } |
| Induction Variable Elimination | Replace variables that change linearly with loop index (i, j = i*4) with a single variable (loop index). |
for(i=0;i<100;i++) { j = i*4; a[j] = ...; } → use i directly to index a[4*i]. |
| Strength Reduction | Replace expensive operations (multiply, divide) with cheaper ones (add, shift). | x = i * 8 → x = i << 3 |
Peephole Optimization:
-
Principle: Examine a small window (peephole) of consecutive instructions and replace with a faster/shorter sequence.
-
Common Reductions:
-
Redundant load/store elimination:
t1 = a; ...; a = t1→ delete. -
Unreachable code:
goto L1; ... L1: ...after agoto. -
Algebraic simplifications:
x = x * 1→ delete;x = x + 0→ delete. -
Sequence reduction:
t1 = t2; t2 = t3→t1 = t3(ift2dead).
-
Basic Blocks & Flow Graphs:
-
Basic Block: A straight-line code sequence with:
-
Single entry (first instruction).
-
Single exit (last instruction is
goto,return, or falls through).
-
-
Construction: Partition code into blocks by leaders (first instruction, target of jump, instruction after jump).
-
Flow Graph: Nodes = basic blocks. Edges = possible control flow between blocks (fall-through, jump).
-
Characteristics: Used for global optimization (data flow analysis).
7.0 RUNTIME ENVIRONMENT & STORAGE ORGANIZATION
Activation Record (AR) / Frame:
-
Purpose: Store information for a single procedure invocation.
-
Typical Layout (grows downwards):
[ Actual Parameters ] <-- Caller pushes [ Return Address ] [ Control Link (Dynamic Link) ] // Points to caller's AR [ Access Link (Static Link) ] // For nested scopes (non-local vars) [ Saved Registers ] [ Local Variables ] [ Temporaries ] [ ... ] <-- SP points here during execution -
Example with Nested Procedures:
procedure P; var x: int; procedure Q; var y: int; begin ... end; begin ... Q ... end;Q's AR has Access Link pointing toP's AR to accessx.
Storage Allocation Strategies:
| Strategy | When | Mechanism | Pros | Cons |
|---|---|---|---|---|
| Static Allocation | Compile-time known sizes (global vars). | Fixed addresses in memory. | Simple, fast. | No recursion; inflexible. |
| Stack Allocation | Procedure calls, local vars. | AR pushed/popped on call/return. | Supports recursion; efficient. | Size of AR must be known at compile time (or fixed). |
| Heap Allocation | Dynamic data (objects, malloc). | Explicit alloc/free or garbage collection. |
Flexible lifetime. | Fragmentation; slower; complex management. |
[!TIP] Key Difference: Stack is for control-driven allocation (procedure calls), Heap is for data-driven allocation (dynamic objects).
Procedure Calls:
-
Call-by-Value: Formal parameter gets a copy of actual argument. Changes inside don't affect caller.
-
Call-by-Reference: Formal parameter is an alias (pointer) to actual argument. Changes affect caller.
-
Parameter Passing: Values (or addresses) are placed in the actual parameters section of the callee's AR.
-
Return Value: Typically placed in a designated register (e.g.,
R0) or in caller's AR.
Memory Management & Garbage Collection:
-
Scope vs. Lifetime:
-
Scope: Where in program text a name is visible (lexical).
-
Lifetime: Time during execution when storage is allocated.
-
-
Garbage Collection: Automatic reclamation of heap memory for unreachable objects. Techniques:
-
Reference Counting: Count pointers to object; collect when count=0. (Fails on cycles).
-
Mark-and-Sweep: Pause program, mark reachable objects from roots, sweep unmarked.
-
Copying (Generational): Divide heap into young/old generations; copy live objects.
-
8.0 ERROR HANDLING
Compiler Error Types (by Phase):
| Phase | Error Examples |
|---|---|
| Lexical | Illegal character, unterminated string/comment. |
| Syntax | Missing ;, mismatched {}, if without then. |
| Semantic | Type mismatch, undeclared variable, incompatible assignment. |
| Logical | Infinite loop, wrong result (compiler bug). |
Error Detection & Reporting:
-
Detection Point: Each phase detects errors in its domain.
-
Lexical: Invalid character pattern.
-
Syntax:
ACTION[i, a] = error. -
Semantic: Type conflict in symbol table.
-
-
Meaningful Messages: Should include:
-
Location: File, line number, column.
-
Nature: "Undeclared identifier 'x'", "Type mismatch: int vs float".
-
Context: Show line of code with error marked.
-
Error Recovery Strategies (Summary):
-
Panic Mode (Global): Discard tokens until synchronizing token (e.g.,
;,}). Simple, prevents infinite loops. -
Phrase Level (Local): Insert/delete/replace a single token to make the rest of the statement syntactically correct. Requires error productions.
-
Error Productions: Add productions like
stmt → error ;to the grammar. Parser reduceserrorwhen it encounters a problematic token. -
Global Correction: (Advanced) Find minimal number of changes (insert/delete/replace) to make program valid. Computationally expensive.
9.0 COMPILER DESIGN TOOLS & CASE STUDIES
Lex & Yacc (Flex & Bison) Workflow:
-
Write Lex file (
*.l): Define patterns & actions (C code) for tokens. -
Write Yacc file (
*.y): Define grammar, precedence, semantic actions (C code). -
Generate:
-
flex lexer.l→lex.yy.c(scanner functionyylex()). -
bison parser.y→parser.tab.c(parser functionyyparse()) andparser.tab.h(token definitions).
-
-
Compile & Link:
gcc lex.yy.c parser.tab.c -o compiler -lm. -
Run:
./compiler < source.c.
Integrating Lex & Yacc:
-
Yacc calls
yylex()to get tokens. -
yylex()returns token type (defined inparser.tab.h). -
Token value (e.g., for
NUM,ID) is stored in global variableyylval.
Step-by-Step Phase Output Example:
Source: a = (x/y) * (y + x - z)
| Phase | Output |
|---|---|
| Lexical | Tokens: ID(a), =, (, ID(x), /, ID(y), ), *, (, ID(y), +, ID(x), -, ID(z), ) |
| Syntax (Parse Tree) | Assign → ID = Expr<br>Expr → Expr * Expr<br>Expr1 → ( Expr2 )<br>Expr2 → ID / ID<br>Expr3 → ( Expr4 )<br>Expr4 → Expr5 + Expr6<br>Expr5 → ID<br>Expr6 → ID - ID |
| Semantic/Symbol Table | a: int, x: int, y: int, z: int (assumed). |
| Intermediate Code (TAC) | t1 = x / y<br>t2 = y + x<br>t3 = t2 - z<br>t4 = t1 * t3<br>a = t4 |
| DAG | Nodes for x, y, z, a. t1 = /(x,y), t2 = +(y,x), t3 = -(t2,z), t4 = *(t1,t3), a = t4. |
| Optimized TAC (if no CSE) | Same as above. |
| Target Code (x86-like) | mov eax, x<br>cdq<br>idiv y<br>mov t1, eax<br>mov eax, y<br>add eax, x<br>sub eax, z<br>imul eax, t1<br>mov a, eax |
10.0 SPECIAL TOPICS (FREQUENT SHORT NOTES)
Quadruples vs. Triples:
| Feature | Quadruples | Triples |
|---|---|---|
| Structure | (op, arg1, arg2, result) |
(op, arg1, arg2) |
| Result Reference | Explicit field (variable name or temp). | Implicit: the triple's position (index). |
| Temporary Management | Creates new temporaries (t1, t2). |
No new names; refers to other triples by (i). |
| Optimization | Easier for global optimizations (CSE, DAG construction). | Harder; need to track indirect references. |
Example: a = b * c + d |
( *, b, c, t1 )<br>( +, t1, d, t2 )<br>( =, t2, -, a ) |
( *, b, c )<br>( +, (1), d )<br>( =, (2), a ) |
Constant Folding:
-
Definition: Evaluating constant expressions at compile time.
-
Example:
x = 2 + 3 * 4→x = 14. -
When: During intermediate code generation or optimization phase.
-
Limitation: Requires operands to be compile-time constants (literals or constant variables).
L-attributed Definitions:
-
Definition: A class of attribute grammars where inherited attributes can only be inherited from parent and left siblings in the parse tree.
-
Significance: Can be evaluated in a single left-to-right pass (suitable for LR parsing with semantic actions).
-
Example Grammar (Simple Assignment):
S → L = R L → * L1 { L1.inh = L.base + 1; L.base = L1.inh; } // Inherited from parent L L → id { L.base = address(id); } R → R1 + T { R.val = R1.val + T.val; }Here,
L1.inhis inherited from its parentL. This is L-attributed.
Peephole Optimization Techniques:
-
Redundant Instruction Elimination: Remove loads/stores of same value.
-
Unreachable Code Removal: Delete code after unconditional jump.
-
Algebraic Simplification:
x = x * 1→ delete;x = x + 0→ delete;x = x << 0→ delete. -
Strength Reduction:
x = y * 8→x = y << 3. -
Sequence Reduction: Combine
t1 = t2; t2 = t3→t1 = t3ift2dead. -
Jump Chaining:
goto L1; L1: goto L2→goto L2.
Input Buffering in Lexical Analysis:
-
Problem:
read()system call is expensive. -
Solution: Use two large buffers (
BUFF_SIZE), read one while scanning the other. -
Sentinel Method: Append a special sentinel character (e.g.,
EOF) at the end of each buffer. Allows checking for buffer boundary with a single test:if (forward == eof) { ... reload buffer ... } if (*forward == '\n') line_num++; -
Benefit: Eliminates need to check
forward < limiton every character access.
Overloading of Functions:
-
Definition: Multiple functions with same name but different parameter types.
-
Resolution (Overload Resolution): At a call site, compiler selects the best match based on:
-
Exact match (same type).
-
Promotion (e.g.,
char→int). -
Standard conversion (e.g.,
int→float). -
User-defined conversion (if applicable).
-
Ellipsis (
...) (last resort).
-
-
Example (C++):
void f(int); // #1 void f(double); // #2 f('a'); // Calls #1 (char promoted to int) f(3.14f); // Calls #2 (float promoted to double)
RGPV EXAM STRATEGY:
-
High-Weightage Topics: DAG construction, SLR/LR table construction, TAC generation (especially for
switch/loops), Lex program, Symbol Table structures, Activation Records, Backpatching. -
Comparison Questions: SLR vs LALR vs LR; Stack vs Heap; Quadruples vs Triples.
-
Example-Based: Always illustrate answers with small, clear examples (like those from past papers).
-
Diagrams: Draw parse trees, DAGs, activation records, basic block flow graphs neatly.
-
Definitions: Start answers with crisp definitions (e.g., "A DAG is...").
Final Reminder: In exams, show all steps for table construction (FIRST/FOLLOW, canonical collection) and DAG building. Partial credit is awarded for correct methodology even if final answer has minor errors.