Compiler Design
105601Module 1: Introduction, Lexical Analysis
Q1a. Consider the statement: int total = 25; Which of the following represents a lexeme? (i) int (ii) 25 (iii) total (iv) All of the Above20262m
Module 1: Introduction, Lexical Analysis
View this question on its own page →Consider the statement: int total = 25; Which of the following represents a lexeme?
(i) int
(ii) 25
(iii) total
(iv) All of the AboveWorked SolutionAnswer: (iv) All of the Above
Explanation
A lexeme is the actual sequence of characters in the source program that matches the pattern of a token. The scanner groups the characters of
int total = 25;into these lexemes:Lexeme Token class intkeyword totalidentifier =assignment operator 25integer constant ;separator So
int,totaland25are all lexemes.Q1b. The regular expression (a \mid b)^* represents: (i) Strings containing only a (ii) Strings containing only b (iii) All strings over \{a, b\}, including \varepsilon (iv) Strings beginning with ab20262m
Module 1: Introduction, Lexical Analysis
View this question on its own page →The regular expression represents:
(i) Strings containing only
(ii) Strings containing only
(iii) All strings over , including
(iv) Strings beginning withWorked SolutionAnswer: (iii) All strings over , including
Explanation
- matches exactly one symbol: either or .
- The Kleene star allows zero or more repetitions of that choice.
So the language is , i.e. every string over the alphabet , with (zero repetitions) included.
Why the other options fail:
- (i), (ii): they allow only one of the two symbols.
- (iv): it forces the string to start with , but also accepts , , etc.
Q1j. Which statement is true regarding DFA and NFA? (i) NFA is strictly more powerful than DFA (ii) DFA and NFA recognize the same class of languages (iii) DFA is strictly more powerful than NFA (iv) Every NFA requires more states than every equivalent DFA20262m
Module 1: Introduction, Lexical Analysis
View this question on its own page →Which statement is true regarding DFA and NFA?
(i) NFA is strictly more powerful than DFA
(ii) DFA and NFA recognize the same class of languages
(iii) DFA is strictly more powerful than NFA
(iv) Every NFA requires more states than every equivalent DFAWorked SolutionAnswer: (ii) DFA and NFA recognize the same class of languages
Explanation
Both DFAs and NFAs recognize exactly the regular languages.
- Every DFA is already an NFA (a special case).
- Every NFA can be converted to an equivalent DFA by the subset construction.
So neither is more powerful, which rules out (i) and (iii). An NFA is often smaller than the equivalent DFA: an NFA with states may need up to DFA states. That is a size difference only, and option (iv) is false because many NFAs need no more states than their DFAs (a DFA is itself an NFA).
Q2a. Explain the working of each phase of compiler in detail with an example.20237m
Module 1: Introduction, Lexical Analysis
View this question on its own page →Explain the working of each phase of compiler in detail with an example.
Worked SolutionThe six phases of a compiler (with an example)
A compiler translates a high-level program into target (machine) code in several phases. They are divided into the front end (source-language dependent) and back end (machine dependent).
Lexical Analysis (Scanner)
- Reads the raw character stream and groups it into meaningful tokens (identifiers, keywords, operators, literals).
- Output: a stream of tokens .
- Uses a symbol table to record identifiers.
- Example: for
position = initial + rate * 60, tokens are:ident(position),=,ident(initial),+,ident(rate),*,num(60).
Syntax Analysis (Parsing)
- Groups tokens into a parse tree / syntax tree according to the grammar.
- Detects syntactic errors (e.g.
x = + * y). - Output: a parse tree (for
position = initial + rate*60we get precedence built in:rate*60is computed first).
Semantic Analysis
- Checks the parse tree for semantic correctness: type checking, name/declaration matching, etc.
- For types: if
position, initial, ratearefloatand10isint, it may insert implicit conversionint→real. - Output: annotated syntax tree.
Intermediate Code Generation
- Produces a machine-independent intermediate representation (e.g. three-address code).
- Example for the above assignment:
t1 = int_to_float(10) t2 = rate * t1 t3 = initial + t2 position = t3
Code Optimization
- Improves the intermediate code, e.g. reduces
t1 = int_to_float(10)because10is a constant that can be folded and converted once. After optimization:t2 = rate * 10.0 position = initial + t2
- Improves the intermediate code, e.g. reduces
Code Generation (Target Code Generation)
- Maps optimized intermediate code onto target machine's assembly instructions, e.g.:
MOVF R2, #10.0 (or LDF) MULF R2, R1 ADDF R2, R0 MOVF position, R2
- Maps optimized intermediate code onto target machine's assembly instructions, e.g.:
Symbol Table is a central data structure maintained throughout all phases, storing names, types, scope, and addresses.
Q2a. Explain the different phases of compilation with a suitable example. Show the input and output produced at each phase.20267m
Module 1: Introduction, Lexical Analysis
View this question on its own page →Explain the different phases of compilation with a suitable example. Show the input and output produced at each phase.
Worked SolutionPhases of a compiler
A compiler translates a source program into target code in a sequence of phases. Two components work with every phase: the symbol table (stores names and attributes) and the error handler (reports errors with line numbers).
MermaidDiagramRendering diagram…Running example:
position = initial + rate * 60whereposition,initial,ratearefloat.Phase Task Output for the example 1. Lexical analysis Scans characters, groups them into lexemes and produces tokens; enters identifiers into the symbol table; removes whitespace and comments <id,1> <=> <id,2> <+> <id,3> <*> <num,60>2. Syntax analysis Checks token order against the grammar and builds a parse/syntax tree Syntax tree (diagram below) 3. Semantic analysis Type checking, declaration checks, type conversions (coercion) 60is int,rateis float, so insertinttofloat(60)4. Intermediate code generation Produces machine-independent code, usually three-address code t1 = inttofloat(60)t2 = id3 * t1t3 = id2 + t2id1 = t35. Code optimization Improves speed/size: constant folding, copy propagation, dead-code removal t1 = id3 * 60.0id1 = id2 + t16. Code generation Selects instructions, allocates registers, emits target code LDF R2, id3MULF R2, R2, #60.0LDF R1, id2ADDF R1, R1, R2STF id1, R1Input and output summary
- Lexical analyzer: character stream to token stream.
- Syntax analyzer: tokens to syntax tree.
- Semantic analyzer: syntax tree to type-checked (annotated) tree.
- Intermediate code generator: annotated tree to three-address code.
- Optimizer: intermediate code to better intermediate code.
- Code generator: optimized intermediate code to target code.
The first three phases form the analysis (front end); the last three form the synthesis (back end).
Syntax tree (phase 2)
MermaidDiagramRendering diagram…Tree after semantic analysis (phase 3): the integer constant is converted to float.
MermaidDiagramRendering diagram…Q2b. Construct a DFA over \{0, 1\} that accepts all strings containing an even number of 0s and an odd number of 1s. Verify the constructed DFA using suitable accepted and rejected input strings.20267m
Module 1: Introduction, Lexical Analysis
View this question on its own page →Construct a DFA over that accepts all strings containing an even number of s and an odd number of s. Verify the constructed DFA using suitable accepted and rejected input strings.
Worked SolutionDFA for: even number of 0s and odd number of 1s over
Idea: track two parities, the parity of the number of 0s and the parity of the number of 1s seen so far. That gives states.
State #0s #1s (start) even even odd even (final) even odd odd odd Reading a
0flips the parity of 0s; reading a1flips the parity of 1s. Accepting state: (even 0s, odd 1s).Transition table
State on 0 on 1 Transition diagram
MermaidDiagramRendering diagram…Formally .
Verification
Input Path Final state 0s / 1s Result 10 / 1 Accepted 0012 / 1 Accepted 100112 / 3 Accepted 011 / 1 Rejected 01102 / 2 Rejected 110 / 2 Rejected 0 / 0 Rejected Every accepted string has an even count of 0s and an odd count of 1s, and every rejected one violates at least one condition, so the DFA is correct.
Q3a. For a lexical analyzer, design regular expressions to recognize the following token classes: (i) Identifiers beginning with a letter and containing letters and digits thereafter. (ii) Unsigned integer constants. (iii) Relational operators <, <=, >, >=, ==, and !=.20267m
Module 1: Introduction, Lexical Analysis
View this question on its own page →For a lexical analyzer, design regular expressions to recognize the following token classes:
(i) Identifiers beginning with a letter and containing letters and digits thereafter.
(ii) Unsigned integer constants.
(iii) Relational operators and .Worked SolutionRegular expressions for the token classes
Let
(i) Identifiers (start with a letter, then letters or digits)
Accepted:
total,x1,rate2023. Rejected:2abc(starts with a digit).(ii) Unsigned integer constants
Accepted:
0,25,1000. Rejected:-5(sign),3.5(not an integer).(iii) Relational operators
Factored form:
The scanner always takes the longest match, so
<=is read as one token, not<followed by=. A single=is the assignment operator, not a relational operator.Recognizer for relop (transition diagram)
MermaidDiagramRendering diagram…Final states:
LT(<),GT(>),LE(<=),GE(>=),EQEQ(==),NE(!=). The statesEQ1andNOTare not final because=alone (assignment) and!alone are not relational operators here.Q9. Write short notes on any two of the following: (a) LEX and YACC (b) Peephole Optimization (c) Symbol Table (d) Predictive parser202314m
Module 1: Introduction, Lexical Analysis
View this question on its own page →Write short notes on any two of the following:
(a) LEX and YACC
(b) Peephole Optimization
(c) Symbol Table
(d) Predictive parserWorked SolutionShort notes (any two)
(a) LEX and YACC
LEX is the lexical-analyzer generator. It takes a specification of token patterns written with regular expressions and produces a C function (e.g.
yylex) that scans the input and returns tokens.- Structure of a LEX program:
declaration %% translation rules %% auxiliary functions. - Each token rule: pattern → action (action = C code, e.g.
return token_id;). - Powers: longest-match rule; a symbol table kept for identifiers/text. Used with YACC. LEX produces the scanner that feeds tokens to the parser.
- Structure of a LEX program:
YACC ("Yet Another Compiler-Compiler") is the parser generator, producing a
LALR(1)(or YACC's default SLR/LALR) parser (functionyyparse) from a CFG grammar in BNF with embedded semantic actions.- Structure:
%{ declarations %} %% productions { semantic action } %% subroutines. - Uses the tokens lexed by LEX; each grammar rule may have a semantic value and user code; supports precedence and associativity declarations (
%left,%right); optionally builds an AST via actions. - Together: LEX gives tokens to YACC, YACC builds the parse tree and schedules the semantic translation.
- Structure:
(b) Peephole Optimization
A simple but effective local, post-optimization technique performed on the target (assembly/machine) code over a small window ("peephole") of adjacent instructions.
Techniques in a peephole optimizer:
- Redundant instruction elimination (dead code): e.g.
MOV R0, R1; MOV R0, R1— delete the second. - Flow-of-control optimizations: e.g. jumps to jumps (
JMP L; ... L: JMP L1) — replace with direct jump. - Algebraic simplifications:
x + 0→x;x*1→ x;x/2` → shift etc. - Reduction in strength: replace expensive ops like
2 * xwithx + xandx^2. - Use of machine idioms: e.g.
X = X + 0removed,X+1replaced byINC/INCandX<<nforX * 2^n. - Constant folding inside window.
Advantage: very cheap, catches a lot; often run repeatedly.
(c) Symbol Table
- A compile-time data structure that maps identifiers (names) to their attributes: name, type, category (variable, function, record/array), address (stack/relative/global offset), scope & lifetime, parameter list, and kind (static, local).
- Operations: insert (add new name), lookup/access (find entry), modify; the table is consulted/modified during every phase — lexical (insert), syntax/semantic (lookup/check type), code gen (get address).
- Organization:
- Lists / hash tables; open (separate chaining) or closed addressing for speed.
- Scoping: block structure ⇒ re-entrant by saved-bottom markers; often a stack of scopes with a
new..pointer (scopestack). - Multiple table organizations: (1) flat global, (2) per-scope linked lists, (3) tree/structural block table (often implemented as stack-based with block index markers).
- Lookup in nested scopes follows lexical chain (innermost outward). Implementations use hash table for names with scope table.
- Applications: optimize code-gen (address known), type checking, overloading, and reporting.
(d) Predictive parser
A predictive parser (or LL(1) table parser) is a non-backtracking top-down parser. It uses a parsing table
M[A][a], the input's next symbola, and a stack of grammar symbols to decide deterministically ("predict") which production to apply without backtracking.- Table-driven: stack holds nonterminals (and
$); at each step, if top of stack is terminal, match with input; if it's a nonterminal, use entryM[top][a]to choose the production forA. - Table is built from FIRST and FOLLOW sets:
M[A][a] = A→α if a∈FIRST(α), or (if α nullable) if a∈FOLLOW(A). - A given grammar can be predictive only when it is LL(1): no multiple entries (no conflict) in any table cell and left-recursion removed + factorized.
- Also called recursive-descent parser when implemented with recursive functions, each nonterminal being one function that calls other via the table (though classic "predictive parser" = table-driven with no backtracking).
- Algorithm: repeat — look at top X and next input a:
- if X is
a, advance both; - if X is nonterminal and M[X][a] = X→Y1..Yk, pop X push Yk…Y1 (reverse feedback);
- if M entry empty → error.
- Accept when stack =
.
- if X is
Characteristics: no backtracking ⇒ linear time (O(n) for LL(1)); works for all LL(1) grammars; fastest top-down method; the classic example is predicting grammar E → T E', etc.