Home › CSE Hub › Compiler Design

Compiler Design

The phases of a compiler — lexical, syntax and semantic analysis, intermediate code, optimization and code generation — plus parsing techniques and the symbol table.

हिंदी में: कंपाइलर हमारे लिखे कोड को मशीन की भाषा में कैसे बदलता है — चरण दर चरण।

Compilers, interpreters and friends

  • A compiler translates the whole source program into a target language (often machine code) before it runs — e.g. GCC for C.
  • An interpreter executes the program statement by statement — e.g. the CPython interpreter.
  • An assembler converts assembly language into machine code; a linker joins object files and libraries; a loader places the program in memory to run.
  • Java uses both: javac compiles to bytecode, and the JVM interprets and JIT-compiles it.

Phases of a compiler

Phase Input → Output What it does
Lexical analysis Characters → tokens Groups characters into tokens (keywords, identifiers, numbers, operators); removes whitespace and comments
Syntax analysis (parsing) Tokens → parse tree Checks the grammar of the language
Semantic analysis Parse tree → annotated tree Type checking, scope rules, declared-before-use
Intermediate code generation Tree → IR Produces machine-independent code such as three-address code
Code optimization IR → better IR Makes code faster or smaller without changing meaning
Code generation IR → target code Chooses instructions and allocates registers

The symbol table (storing names, types and scopes) and the error handler are used by every phase. The first phases form the front end (language-dependent); the later ones form the back end (machine-dependent).

Example

For total = price + rate * 10, the lexer produces tokens such as id(total) = id(price) + id(rate) * num(10). The parser builds a tree where * binds tighter than +. Three-address code might be:

t1 = rate * 10
t2 = price + t1
total = t2

Lexical analysis

Token patterns are described with regular expressions and recognised with finite automata. Tools such as Lex/Flex generate lexers automatically.

Parsing

Top-down parsers build the tree from the start symbol: – Recursive descent, and LL(1) predictive parsing using FIRST and FOLLOW sets. – The grammar must have no left recursion and must be left-factored.

Bottom-up parsers reduce the input back to the start symbol (shift–reduce): – LR(0), SLR(1), LALR(1), CLR(1) — in increasing power. LALR(1) is used by Yacc/Bison.

Syntax-directed translation

Semantic rules attached to grammar productions compute attributes: – Synthesized attributes take values from children. – Inherited attributes take values from the parent or siblings.

Code optimization techniques

  • Constant folding: x = 2 * 3 → x = 6.
  • Common subexpression elimination: compute a + b once and reuse it.
  • Dead code elimination: remove code whose result is never used.
  • Loop optimizations: move loop-invariant code out of loops, strength reduction (replace * with +), loop unrolling.
  • Peephole optimization: improve short sequences of target instructions.

Runtime environment

Activation records on the stack hold parameters, local variables and return addresses for each function call; dynamically allocated objects live on the heap.

Quick revision

  • Lexer → parser → semantic analyser → IR → optimizer → code generator.
  • LL parsers are top-down; LR parsers are bottom-up.
  • LR(0) ⊂ SLR(1) ⊂ LALR(1) ⊂ CLR(1) in power.
  • Three-address code has at most one operator on the right-hand side.