Writing a CFG

Context: FIT2014_MOC · turning a language description into a grammar · the assessable counterpart to Finding Regular Expressions, one tier up Problem it solves: given a language, write production rules that generate exactly it.

Quick Revision

  • 🎯 Trigger: a language with nesting or matching or a running count ➔ find an inductive definition (“a string is either , or … built from smaller strings”) and transcribe each case into a rule.
  • ⚡ Key Constraint: the recursion must be structural — every rule should shrink toward the base case , and the nonterminals should capture the invariant that defines membership.

📐 The recipe

  1. Find an inductive definition of the language: a base case plus ways to build bigger members from smaller ones.
  2. One rule per case. The base case becomes (or a terminal); each recursive case becomes a rule whose right side contains nonterminal(s) for the smaller pieces.
  3. Introduce a nonterminal per “type” of string when membership depends on a running property (imbalance, parity, phase).
  4. Check the extremes — does the grammar produce and the shortest non-empty members? Does it avoid non-members?

🧱 Worked example — PARENTHESES (the Dyck language)

Language ➔ all strings of correctly matched parentheses: (non-members: , ).

Inductive definition ➔ a string of parentheses is one of:

  • the empty string ;
  • where is a string of parentheses;
  • where are strings of parentheses.

Grammar (transcribe the three cases):

  • Where does the matching go? ➔ any non-empty balanced string starts with ; its partner is either at the very end (rule ) or before the end (rule ) — exactly the two recursive cases.

✍️ Practice

⚠️ Common Mistakes

  • 💡 Concatenation needs its own rule (or similar) is what lets members sit side by side; without it is underivable.
  • 💡 Don’t over-generate ➔ a sloppy grammar like would produce ; the wrap rule is what enforces matching.
  • 💡 Track the invariant with nonterminals ➔ for counting languages (EQUAL), one nonterminal per imbalance class; for phase languages, one per phase.
  • 💡 Always allow the base case ➔ forgetting silently excludes the empty word (and can break recursion that bottoms out at ).

🧠 Active Recall