Phases of Compiler Explained: Worked Example from Tokens to Target Code

Follow one four-line program through lexical, syntax and semantic analysis, then see its intermediate code optimized to a final stored value of 24.0.

KnowledgeGate Team

Exam prep & CS education

Updated 24 Sep 20266 min read

Memorising six compiler phase names is easy. Questions become harder when they ask what each phase consumes, what it produces, or where an error is detected. Intro to Compilers (Compiler Design): The Six Phases Traced with Worked Examples explains the compiler-versus-interpreter distinction and maps the six stages. The four-line program below adds an exact value-bearing trace: 66 source characters and 21 lexical tokens become a typed tree, three-address code, target operations and 24.0 in storage.

1. Phases of a Compiler: Map the Pipeline Before Memorising It

The exam-safe data flow is: source character stream to lexical analyser to token stream to syntax analyser to syntax tree to semantic analyser to typed tree to intermediate-code generator to IR to optimizer to improved IR to code generator to target code.

Assume these four statements appear inside main; its function header and braces are outside the counted excerpt:

c
int base = 12;
int rate = 3;
float total;
total = base + rate * 4;

Multiplication binds first, so rate * 4 = 3 * 4 = 12. Then base + 12 = 12 + 12 = 24. A permitted widening conversion stores that value as 24.0 in total.

The symbol table and error handler support several phases, so they are not two extra boxes in the linear chain. Also, a phase is not a pass. An implementation may combine phases in one pass or revisit a phase during several passes.

2. Lexical Analysis: Turn 66 Source Characters into 21 Tokens

Count the program exactly as displayed, with one newline after each of the first three lines and no final newline. It has 66 characters: 13 spaces, 3 newlines and 50 other characters. For this example, the lexer discards the spaces and newlines, then recognises these tokens:

Code
(KEYWORD,int) (ID,base) (ASSIGN,=) (INT,12) (SEMI,;)
(KEYWORD,int) (ID,rate) (ASSIGN,=) (INT,3)  (SEMI,;)
(KEYWORD,float) (ID,total) (SEMI,;)
(ID,total) (ASSIGN,=) (ID,base) (PLUS,+) (ID,rate) (STAR,*) (INT,4) (SEMI,;)

The line-wise count is 5 + 5 + 3 + 8 = 21. An end-of-file marker is not counted as a source token here. Notice that base is one identifier token and 12 is one integer-literal token. Whitespace separates lexemes, but does not itself become a token.

Token patterns are commonly written as regular expressions and recognised using finite automata. Under this teaching language, total = base @ 4; contains a lexical error because no token pattern accepts @.

Once the count is clear, test the same boundary with Lexical Analysis MCQs: 10 Solved Compiler Design.

3. Syntax Analysis: Build the Tree That Enforces Precedence

The parser consumes tokens, not raw source characters. A compact grammar for the final assignment is:

Code
stmt   -> id = expr ;
expr   -> expr + term | term
term   -> term * factor | factor
factor -> id | int_literal

Its abstract syntax tree is =(total, +(base, *(rate, 4))). Assignment is the root. Addition is the root of the right side, with multiplication nested below it. That shape forces rate * 4 to happen first.

The competing tree =(total, *(+(base, rate), 4)) would calculate (12 + 3) * 4 = 60, so it does not represent the precedence encoded by this grammar. Parsing practice can reinforce the distinction between those tree shapes.

In total = base + ;, every character can be tokenised, but no expression follows +. That makes it a syntax error. Programming-language syntax is commonly described by a context-free grammar and recognised by a parser with stack, or pushdown-automaton, power.

4. Semantic Analysis and the Symbol Table: Check Types and Insert the Conversion

Assume this teaching target gives both int and float four bytes. One illustrative symbol table is:

Name

Type

Scope

Offset

base

int

main

0

rate

int

main

4

total

float

main

8

Exact fields and offsets depend on the implementation. The stable point is that later phases can retrieve each name's declaration, type and storage information.

Now annotate the expression bottom-up. rate:int * 4:int gives 12:int. Then base:int + 12:int gives 24:int. Because this language permits widening from int to float, semantic analysis inserts a conversion before assignment:

Code
=(total:float, int_to_float(+(base:int, *(rate:int, 4:int))))

By contrast, total = base + "4"; can match the assignment grammar, but adding an integer and a string is a semantic type error under these rules. Declaration-before-use, scope lookup, function-argument agreement and assignment compatibility are also semantic checks. That does not mean every possible language error must be detected at compile time.

Compiler pipeline for total = base + rate * 4, running from 21 tokens through the typed tree and IR to LOADF and STORE.

5. Intermediate Code, Optimization and Target Code: Compute Every Value

Intermediate-code generation can express the typed tree as four three-address statements:

Code
t1 = rate * 4
t2 = base + t1
t3 = int_to_float t2
total = t3

Evaluate them in order. Since rate = 3, t1 = 3 * 4 = 12. Since base = 12, t2 = 12 + 12 = 24. Widening gives t3 = 24.0, so total = 24.0.

Now assume base and rate are not reassigned, neither is volatile, their addresses do not escape, and both initialisers are visible to the optimizer. Constant propagation and constant folding can reduce the improved IR to total = 24.0. Optimization must preserve program meaning, not merely shorten the written form.

Unoptimized target operations on an illustrative register machine could be:

Code
LOAD R1,[rate]
MUL R1,R1,#4      ; R1 = 12
LOAD R2,[base]
ADD R3,R2,R1      ; R3 = 24
I2F F0,R3         ; F0 = 24.0
STORE [total],F0

After optimization, the sequence may become:

Code
LOADF F0,#24.0
STORE [total],F0

Instruction selection, register allocation and exact mnemonics depend on the target architecture. The stored result remains 24.0.

Before and after optimization: four IR rows and six target operations collapse to total = 24.0 with LOADF and STORE.

6. How Compiler-Phase Questions Test the Boundaries

Phases of a Compiler for GATE: One Statement Traced Through Every Phase is the canonical phase-to-artifact reference and follows a single floating-point assignment. This trace instead exposes source-character and token counts, declared initial values, offset assumptions and full constant propagation to 24.0. Use the input and output to identify a phase before looking at the options:

Prompt clue

Answer

Trap

characters to tokens

lexical analysis

lexer produces tokens but consumes characters

tokens to syntax tree

syntax analysis

parser does not consume source characters directly

typed or annotated tree

semantic analysis

legal grammar does not guarantee legal types

IR to improved IR

optimization

output is still IR

improved IR to target instructions

code generation

target details are machine-dependent

The phase boundaries are easy to distinguish. The token count is 21. The assignment's right side is +(base, *(rate,4)). The syntactically valid string addition fails during semantic analysis. The constant-folded result is total = 24.0, not 60.0.

Lexical, syntax and semantic analysis are front-end work. IR optimization is commonly middle-end work, and final code generation is back-end work. Intermediate-code generation is the bridge and may be grouped differently across textbooks and implementations. Drill these boundaries until the input and output of each phase are automatic. How heavily any exam weights the topic is a separate matter, and only the official notification settles that.

7. Phases of Compiler Explained: The Short Version and Next Step

Keep the chain in one breath: characters to tokens to tree to typed tree to IR to improved IR to target code. With one newline after each of the first three lines and no final newline, the program has 66 characters, 21 tokens, four unoptimized IR statements, one optimized assignment and final value 24.0.

For revision, redraw the pipeline without looking, reproduce the token count, and explain both the widening conversion and the valid replacement with 24.0. Then use GATE Guidance by Sanchit Sir for structured Compiler Design study, or the GATE CS Exam Preparation Courses & Test Series category page for the wider subject plan.