Yasir Explains/Compiler Design/Grammars and Expressions/Concepts of Languages and Grammars
Grammars and Expressions

Concepts of Languages and Grammars

On this page

What is a Formal Language?What is a Grammar?Production Rules Up CloseThe Chomsky HierarchyWhy Grammars Matter in Compilers
Grammars and Expressions

Concepts of Languages and Grammars

Learn what a formal language is, how a grammar defines it, and how the Chomsky Hierarchy organises all languages into four nested levels.

What is a Formal Language?

In everyday life, a language is a system of words and rules we use to communicate. In computer science, a formal language is a mathematically precise version of that idea.

A formal language is built from three building blocks:

1. Alphabet (Σ) A finite, non-empty set of symbols. These are the only characters that may appear in strings.

AlphabetSymbols
Σ = {0, 1}binary digits
Σ = {a, b}two letters
Σ = {a, b, c}three letters

2. String (Word) A finite sequence of symbols taken from the alphabet. The empty string ε contains zero symbols.

Example
Σ = {a, b}
Some strings over Σ:
ε (empty string, length 0)
a (length 1)
ab (length 2)
aab (length 3)
bba (length 3)

3. Language (L) A (possibly infinite) set of strings over Σ.

Example
L₁ = {ab, aabb, aaabbb, ...} ← strings with equal a's then b's
L₂ = {ε, a, aa, aaa, ...} ← any number of a's (including none)
L₃ = {0, 1, 00, 01, 10, 11} ← all binary strings of length 1 or 2

Two special languages:

  • ∅ — the empty language (contains no strings at all)
  • {ε} — a language containing only the empty string (one member, but it has zero symbols)

What is a Grammar?

A grammar is a finite set of rules that tells you how to generate (or recognise) all strings in a language. Instead of listing every string (which is impossible when the language is infinite), you write a small rulebook.

Formally, a grammar is a 4-tuple:

Example
G = ( V, T, P, S )
SymbolNameMeaning
VVariables / Non-terminalsPlaceholder symbols — get replaced by rules
TTerminalsActual output symbols from the alphabet Σ
PProduction RulesThe replacement rules themselves
SStart SymbolThe one non-terminal you begin with (S ∈ V)

Rule of thumb: Terminals are what end up in the final string. Non-terminals are scaffolding that help you build it.


A Concrete Example

Let us define a grammar for the language L = {aⁿbⁿ | n ≥ 1} = {ab, aabb, aaabbb, …}

Example
G = ( V, T, P, S )
V = { S }
T = { a, b }
S = S
P = { S → aSb, S → ab }

Reading the rules:

  • S → ab means "S can be replaced by the string ab"
  • S → aSb means "S can be replaced by aSb, wrapping itself with one a on the left and one b on the right"

Deriving "aaabbb":

Example
S
→ aSb (apply S → aSb)
→ aaSbb (apply S → aSb again)
→ aaabbb (apply S → ab)

Each arrow (→) is one derivation step where we apply exactly one production rule.

Production Rules Up Close

A production rule has the form:

Example
left-hand side → right-hand side
α → β
  • α contains at least one non-terminal (it is what gets replaced)
  • β can be any combination of terminals and non-terminals, or ε

The process of applying rules repeatedly is called derivation. Using ⟹* to mean "derives in zero or more steps":

Example
If S ⟹* w and w contains only terminals,
then w is a sentence (valid string) of the language.

Sentential Form vs Sentence

TermDefinitionExample (from above grammar)
Sentential formAny string reachable from S, may still have non-terminalsS, aSb, aaSbb
SentenceA sentential form with NO non-terminals leftab, aabb, aaabbb

The Language Generated by G

Example
L(G) = { w ∈ T* | S ⟹* w }

This reads: "The language of G is the set of all terminal strings w that can be derived from the start symbol S."

The Chomsky Hierarchy

Noam Chomsky classified all grammars (and therefore all formal languages) into four types based on how restrictive their production rules are. Each type is a subset of the one above it.

Example
Type 0 ⊇ Type 1 ⊇ Type 2 ⊇ Type 3

Type 0 — Unrestricted

No restrictions on rules. Most powerful.

▼

Type 1 — Context-Sensitive

Rules can depend on surrounding context.

▼

Type 2 — Context-Free

Rules replace one non-terminal at a time.

▼

Type 3 — Regular

Rules are linear (left or right). Least powerful.

TypeGrammarLanguage ClassRecogniserExample Language
0UnrestrictedRecursively EnumerableTuring MachineAny computable language
1Context-Sensitive (CSG)Context-SensitiveLinear-Bounded Automaton{aⁿbⁿcⁿ | n ≥ 1}
2Context-Free (CFG)Context-FreePushdown Automaton{aⁿbⁿ | n ≥ 1}
3Regular (RG)RegularFinite Automaton (DFA/NFA){aⁿ | n ≥ 0}

Key insight: As you move down the hierarchy, the rules become more restricted, the languages become simpler, and the machines needed to recognise them become less powerful.

Programming languages are mostly context-free (Type 2). Regular expressions match regular languages (Type 3). Most compilers use a CFG for parsing.

Why Grammars Matter in Compilers

Every programming language you write code in is defined by a grammar. That grammar is what allows a compiler to:

  1. Check syntax — "Is this a valid statement?"
  2. Build a parse tree — break the code into a structured tree
  3. Generate code — walk the tree to produce output
Example
Source code: int x = a + b;
Grammar rule: Declaration → Type Identifier = Expr ;
Grammar rule: Expr → Expr + Term | Term
Grammar rule: Term → Identifier | Number
Parse tree:
Declaration
/ | | | \
Type ID = Expr ;
(int) (x) / \
Expr Term
(a) (b)
\ /
(+)

Without a grammar, there is no systematic way to understand the structure of source code. The grammar is the backbone of every compiler's front end.