Context-Free Grammars
1971 words
10 min read
Visual companion
Python
Type and operator map
Python Week 1: the first filter for runtime behavior
View
Revision summary
What this note is really saying
Short form
# Context-Free Grammars ## ๐ฏ Learning Objectives - Design context-free grammars for various languages - Generate parse trees and derivations (leftmost, rightmost) - Detect and eliminate ambiguity in grammars - Convert CFGs to Chomsky Normal Form - Apply closure properties of context-free languages * * * ## 1. Intro...

Context-Free Grammars
๐ฏ Learning Objectives
- Design context-free grammars for various languages
- Generate parse trees and derivations (leftmost, rightmost)
- Detect and eliminate ambiguity in grammars
- Convert CFGs to Chomsky Normal Form
- Apply closure properties of context-free languages
1. Introduction to CFGs
1.1 Intuition
A context-free grammar is like a set of LEGO building instructions. You start with one piece (the start symbol) and repeatedly replace pieces with more detailed assemblies using rules (productions). The rules are "context-free" because you can apply them regardless of what's around the symbol โ just like LEGO instructions don't depend on what other bricks are nearby.
CFGs describe programming languages, natural language syntax, and protocols. They're more powerful than regular expressions/DFAs โ they can handle nested structures like parentheses, HTML tags, and mathematical expressions.
1.2 Formal Definition
A CFG is a 4-tuple G=(V,ฮฃ,R,S) where:
- V: Finite set of variables (nonterminals)
- ฮฃ: Finite set of terminals (alphabet)
- R: Finite set of rules (productions) of form Aโฮฑ where AโV,ฮฑโ(Vโชฮฃ)โ
- SโV: Start symbol
1.3 Derivation Example
Grammar for balanced parentheses: SโSSโฃ(S)โฃฮต
Derivation of "( ) ( )":
| Step | Sentential Form | Rule Applied |
|---|---|---|
| 1 | S | Start |
| 2 | SS | S โ SS |
| 3 | (S)S | S โ (S) |
| 4 | ()S | S โ ฮต |
| 5 | ()(S) | S โ (S) |
| 6 | ()() | S โ ฮต |
(Diagram)
2. Derivation Types
2.1 Leftmost vs. Rightmost Derivation
| Type | Description | Example (for Sโ0S1โฃฮต ) |
|---|---|---|
| Leftmost | Replace leftmost variable first | Sโ0S1โ00S11โ0011 |
| Rightmost | Replace rightmost variable first | Sโ0S1โ0S01โ0011 |
2.2 Worked Example
Grammar: EโE+EโฃEโEโฃ(E)โฃa
Leftmost derivation of a+aโa: EโE+Eโa+Eโa+EโEโa+aโEโa+aโa
Rightmost derivation of a+aโa: EโE+EโE+EโEโE+EโaโE+aโaโa+aโa
Note: The parse tree for both derivations is the same:
(Diagram)
3. Ambiguity
3.1 Intuition
A grammar is ambiguous if a string has more than one parse tree. This is bad for programming languages โ if a compiler can parse
3 + 4 ร 5 in two ways, it gives different results (either 35 or 23).3.2 Ambiguous Grammar
The expression grammar EโE+EโฃEโEโฃ(E)โฃa is ambiguous.
Two parse trees for a+aโa:
Tree 1 (addition first): (a+a)โa โ evaluates as (a+a)โa Tree 2 (multiplication first): a+(aโa) โ evaluates as a+(aโa)
3.3 Eliminating Ambiguity
Unambiguous grammar (respects precedence):
- EโE+TโฃT
- TโTโFโฃF
- Fโ(E)โฃa Now a+aโa has only one parse tree (multiplication binds tighter).
| Rule | Level | Associativity |
|---|---|---|
| EโE+T | Lowest precedence | Left-associative |
| TโTโF | Medium precedence | Left-associative |
| Fโ(E)โฃa | Highest precedence | โ |
4. Chomsky Normal Form (CNF)
4.1 Intuition
CNF is a simplified form where every rule has exactly two nonterminals or one terminal. This restriction makes parsing algorithms (like CYK) simpler โ they only need to consider combining two adjacent sub-results, like binary tree operations.
4.2 CNF Rules
Every production is of the form:
- AโBC (two nonterminals)
- Aโa (one terminal)
- Sโฮต (only if ฮต is in the language, and S never appears on RHS)
4.3 Conversion Algorithm
Step 1: Add new start variable S0โ Step 2: Eliminate ฮต-rules (Aโฮต) Step 3: Eliminate unit rules (AโB) Step 4: Convert remaining rules to CNF
4.4 Worked Example
Original grammar: SโASAโฃaB AโBโฃS Bโbโฃฮต
Step 1 โ New start: S0โโS SโASAโฃaB AโBโฃS Bโbโฃฮต
Step 2 โ Eliminate ฮต-rules (Bโฮต): Remove Bโฮต. For each rule with B on RHS, add version without B: SโaB becomes SโaBโฃa AโB becomes AโBโฃฮต (but we also need to handle ฮต in A)
Step 3 โ Eliminate unit rules: S0โโS, AโB, AโS are unit rules. Replace: AโB (and Bโb) with Aโb. Etc.
Step 4 โ Convert to CNF: S0โโAS1โโฃaBโฃa (where S1โ=SA) ...
Final CNF: S0โโAS1โโฃaBโฃa S1โโSA SโAS1โโฃaBโฃa AโbโฃAS1โโฃaBโฃa Bโb
5. Common Pitfalls
Pitfall 1: Left Recursion in Top-Down Parsing
The mistake: Using AโAฮฑ (left recursion) in a grammar for top-down parsing.
Why students make it: Left recursion is natural for left-associative operations (EโE+T).
How to catch it: Top-down parsers (like LL parsers) infinite loop on left recursion.
Correct approach: Use right recursion or EBNF for top-down grammars, or use table-driven parsers (LR) that handle left recursion.
Pitfall 2: Confusing Derivation with Parse Tree
The mistake: Thinking two different derivations mean the grammar is ambiguous.
Why students make it: Different derivation orders (leftmost vs. rightmost) give different derivations.
How to catch it: Ambiguity means different parse trees, not different derivation orders. Leftmost and rightmost derivations of the same string may differ but have the same parse tree.
Correct approach: Two derivations with the same parse tree โ ambiguity. Different parse trees = ambiguity.
Pitfall 3: Incorrect ฮต-Rule Elimination
The mistake: Removing ฮต-rules without adding replacement rules for all affected productions.
Why students make it: The algorithm seems simple โ just remove ฮต and add new versions.
How to catch it: Some derivations that were valid before elimination become invalid because required replacements were missed.
Correct approach: Find all nullable variables systematically. For every rule AโX1โX2โ...Xnโ, generate all combinations of replacing nullable Xiโ with ฮต.
6. Key Concepts Reference
| Concept | Definition | Application |
|---|---|---|
| CFG | G=(V,ฮฃ,R,S) | Describes context-free languages |
| Derivation | Step-by-step replacement of variables | Generating strings |
| Parse tree | Tree representation of derivation | Syntax analysis |
| Ambiguity | Multiple parse trees for same string | Problem for compilers |
| CNF | AโBC or Aโa only | CYK parsing algorithm |
| Leftmost/Rightmost | Order of variable replacement | Derivation strategies |
7. ๐ Practice Questions
Q1: Design a CFG for L = {0^n1^n | n โฅ 0}.Answer: S โ 0S1 | ฮตDerivation for 0011: S โ 0S1 โ 0(0S1)1 โ 00ฮต11 = 0011 Q2: Is the grammar S โ 0S1 | 0S | ฮต ambiguous?Answer: Yes. The string "01" can be derived in two ways: (1) S โ 0S1 โ 0(ฮต)1 = 01 (2) S โ 0S โ 0(0S1) โ 0(0ฮต1) = 001 โ wait that's "001", not "01".Actually let me re-check. For grammar S โ 0S1 | 0S | ฮต: String "01": S โ 0S1 โ 0ฮต1 = 01 (only this way) String "0": S โ 0S โ 0ฮต = 0 (only this way) String "00": S โ 0S โ 0(0S) โ 0(0ฮต) = 00 OR S โ 0S โ 0(0ฮต) = 00 (same) This grammar is actually unambiguous. Q3: Convert S โ aSa | bSb | ฮต to CNF.Answer:Step 1: New start Sโ โ S. Step 2: No ฮต-rules (except S โ ฮต, which is already in CNF form). Step 3: No unit rules. Step 4: Convert: S โ aSa becomes S โ AโAโ where Aโ โ a, Aโ โ Sa, and Sa โ S Aโ Similarly for bSb.Final CNF: Sโ โ AโAโ | AโAโ | ฮต S โ AโAโ | AโAโ | ฮต Aโ โ a Aโ โ SAโ Aโ โ b Aโ โ SAโ(where Aโ = Sa, Aโ = Sb) Q4: Why are CFGs called "context-free"?Answer: They are "context-free" because the replacement rule for a variable A โ ฮฑ can be applied regardless of the surrounding symbols (context). Contrast with context-sensitive grammars where the rule may depend on neighboring symbols (e.g., aA โ bc means "replace A with c only if preceded by a"). Q5: Design a CFG for L = {a^i b^j c^k | i โ j}.Answer: This is tricky because we can't directly compare. Approach: generate a^i b^j where i โ j, then add matching c's.S โ AC | BC A โ aA | a (at least one a) C โ cC | ฮต (any number of c's) B โ bB | b (at least one b)Wait, this doesn't capture i โ j properly. Better:S โ aSc | aXc | Y (where X generates more a's, Y generates more b's) X โ aX | a Y โ bYc | bY | bActually let me think again:L = {a^i b^j c^k | i โ j}S โ aSc | T T generates {a^i b^j | i โ j} (with extra c's? No, c's handled by S)Better approach: S โ aSc | U U โ aU | aUb | Ub | ab (generate strings where a count โ b count)Hmm, this is complex. Let me simplify:S โ aSc | T T โ aT | aTb | Tb | ab But T needs to ensure i โ j.The standard approach: generate three regions separately: S โ aSc | T (S matches c's with outer a's) T โ aTb | A | B (T generates inner a's and b's) A โ aA | a (more a's) B โ bB | b (more b's)This generates a^i b^j c^k where i = (k + #inner a's), j = #inner b's, and if inner a's โ inner b's then i โ j. Q6: What is the Pumping Lemma for context-free languages?Answer: If L is a CFL, there exists p > 0 such that any string s โ L with |s| โฅ p can be written as s = uvxyz where:
- |vy| โฅ 1 (at least one of v or y is non-empty)
- |vxy| โค p (the pumped region is bounded)
- uv^i xy^i z โ L for all i โฅ 0
This differs from the regular pumping lemma: we're pumping TWO parts (v and y) simultaneously, reflecting the binary branching structure of CFG parse trees. Q7: Design a CFG for the language of palindromes over {0,1}.Answer: S โ 0S0 | 1S1 | 0 | 1 | ฮตThis generates strings that read the same forwards and backwards. For "0110": S โ 0S0 โ 0(1S1)0 โ 0(1ฮต1)0 = 0110 Q8: Why is the grammar E โ E + T | T, T โ T * F | F, F โ (E) | a unambiguous for expressions?Answer: It enforces precedence: + is at the top level (E), * is one level lower (T), and atoms/literals are innermost (F). Since each string forces a specific derivation order (you must expand E first to add, T to multiply, F for parentheses), there's exactly one parse tree. Left recursion in E โ E + T forces left associativity โ there's no alternative way to group the same string.
8. ๐ Cross-References
- Week 3 - DFA Minimization: Regular languages โ CFGs
- Week 5 - Pushdown Automata: PDA = CFG equivalence
- Week 6 - Turing Machines: Beyond context-free
- BSCS4032 (Compiler Design): Parse trees, top-down parsing Join Discord PreviousDFA Min & Pumping LemmaNextCFG โ PDA