GATE/Compiler Design/Lexical Analysis
Easy14 min readCompiler Design

Lexical Analysis

Lexical analysis is the first phase of a compiler — it reads source characters and produces tokens. GATE tests regular expressions for tokens, DFA construction, and the role of the lexer.

Key Points

  • ·Lexer (scanner): reads characters, groups into tokens (lexemes with token class)
  • ·Token classes: keyword, identifier, integer literal, float literal, operator, punctuation
  • ·Regular expressions describe token patterns; DFA/NFA implement the recogniser
  • ·Lexer construction: RE → NFA (Thompson's) → DFA (subset construction) → minimise DFA
  • ·Longest match rule: lexer picks longest possible token
  • ·Symbol table: lexer inserts identifiers; type and scope filled later
  • ·Handles whitespace/comments: typically discarded (not passed to parser)

What is a Compiler?

Analogy: A translator who converts a book from French to English. But a compiler does more — it also checks that the French makes grammatical sense (syntax), that the sentences are meaningful (semantics), and produces an efficient English translation (code generation).

Compiler phases:

Source code
    ↓  Lexical Analysis (Lexer)     → Token stream
    ↓  Syntax Analysis  (Parser)    → Parse tree / AST
    ↓  Semantic Analysis            → Annotated AST
    ↓  Intermediate Code Generation → 3-address code
    ↓  Code Optimisation            → Optimised IR
    ↓  Code Generation              → Machine code

Lexical Analysis — Breaking Code Into Words

Analogy: Reading a sentence word by word. "The quick brown fox" → [The] [quick] [brown] [fox]. The lexer does this for code: int x = 5; → [int] [x] [=] [5] [;]

Input:  int count = 42 + base;

Output tokens:
  (KEYWORD,     "int")
  (IDENTIFIER,  "count")
  (ASSIGN,      "=")
  (INTEGER,     "42")
  (PLUS,        "+")
  (IDENTIFIER,  "base")
  (SEMICOLON,   ";")

Tokens, Lexemes, and Patterns

Pattern:  A rule describing the form of a token
          (described by a regular expression)

Lexeme:   The actual character sequence matching the pattern

Token:    (token_type, lexeme) pair sent to parser

Example:
  Pattern for IDENTIFIER: [a-zA-Z_][a-zA-Z0-9_]*
  Lexeme:                  "count"
  Token:                   (IDENTIFIER, "count")

Regular Expressions for Tokens

Integer literal:   [0-9]+
Float literal:     [0-9]+.[0-9]+
Identifier:        [a-zA-Z_][a-zA-Z0-9_]*
Whitespace:        [    
]+            ← usually discarded
Keywords:          if | else | while | for | int | float ...
Single-line comment: //[^
]*          ← discarded

Operator shortcuts:
  +    *    ?    represent one-or-more, zero-or-more, zero-or-one
  [abc] means a or b or c
  [0-9] means any digit
  [^x]  means any character EXCEPT x

Building a Lexer: RE → NFA → DFA

Step 1: Thompson's Construction (RE → NFA)
  Each regular expression is converted to a small NFA
  with ε-transitions connecting pieces

Step 2: Subset Construction (NFA → DFA)
  Combine NFA states into DFA states (each DFA state = set of NFA states)
  Worst case: 2^n DFA states from n-state NFA (usually much fewer)

Step 3: DFA Minimisation
  Merge indistinguishable states → smaller, faster DFA

Step 4: Run the DFA on input
  O(n) time for input of length n — very efficient!

Longest Match and Priority Rules

When multiple patterns could match, the lexer applies two rules:

Rule 1: LONGEST MATCH WINS
  Input: "count123"
  Pattern "count" matches 5 chars, "[a-z][a-z0-9]*" matches 8 chars
  → Lexer picks the longer match: "count123" as IDENTIFIER

  Input: ">>"
  Could be: ">" followed by ">" (two GREATER_THAN)
  But ">>" is a longer match: RIGHT_SHIFT token
  → Lexer picks ">>" as RIGHT_SHIFT

Rule 2: TIE-BREAKING BY RULE ORDER (keywords before identifiers)
  Input: "if"
  Matches both KEYWORD pattern AND IDENTIFIER pattern (equal length)
  → Rule listed first wins: KEYWORD "if"

  Input: "iflag"
  Matches IDENTIFIER pattern (8 chars), KEYWORD "if" only matches 2 chars
  → Longest match: "iflag" is IDENTIFIER (not keyword "if"!)

Symbol Table

The lexer creates symbol table entries for identifiers:

Symbol table maps: name → (type, scope, address, ...)

Lexer fills in: name
Later phases fill in: type (semantic analysis), address (code generation)

Scoping: nested scopes represented as a stack of hash tables
  Inner scope can shadow outer scope (same name, different meaning)

Lexer vs Parser — What Each Can Handle

Lexer uses FINITE AUTOMATA (Regular Languages):
  ✓ Token patterns: identifiers, numbers, keywords, operators
  ✗ Nested structures: balanced parentheses, matched braces
  ✗ Context dependencies: cannot check if variable was declared

Parser uses PUSHDOWN AUTOMATA (Context-Free Languages):
  ✓ Nested structures: if-else, function definitions, expressions
  ✓ Matching: { with }, ( with )
  ✗ Context: cannot check types or declarations

Quick Check

Q1. Input: "while123". Is this a keyword or identifier? Answer: Identifier. Longest match: "while123" (9 chars) matches IDENTIFIER. "while" keyword only matches 5 chars. Longest match wins.

Q2. How many memory accesses does a DFA-based lexer require per character? Answer: O(1) per character — just look up the next state in the DFA transition table. Total: O(n) for input of length n.

Q3. Why cannot a lexer handle balanced parentheses? Answer: Balanced parentheses require COUNTING the depth (how many are open). Finite automata have finite memory (finite states) and cannot count arbitrarily. A pushdown automaton (with a stack) is needed.

Key Formulas

  • Thompson's NFA size: O(|RE|) states for a regular expression
  • Subset construction: At most 2^n DFA states from n-state NFA

GATE Exam Tips

  • Lexer uses finite automata (regular languages) — cannot handle nested/recursive structures.
  • Keywords beat identifiers on tie (same length) — keywords must be listed first in LEX.
  • Longest match: ">>" in C++ is RIGHT_SHIFT, not two ">" tokens.
  • GATE: "can lexer handle balanced parentheses?" — NO, needs pushdown automaton (PDA).

Finished reading this topic?

Mark it complete to track your study progress.