Frontend

Grammar & Syntax

Writing down what a valid program looks like: productions, derivations, EBNF, and the ambiguity that precedence and associativity exist to resolve.

Formal Grammars
▶ lab

A grammar is a finite set of rules that decides an infinite set of token sequences. Writing one down separates "what is a legal program" from "how do I recognise one", and that separation is the reason a language can have more than one implementation.

Q · Why write a grammar down at all instead of just writing the parser?
Productions & Derivations
▶ lab

A derivation is the proof that a token sequence is in the language. Leftmost and rightmost derivations are the two canonical orders, and they are exactly the orders that top-down and bottom-up parsers reconstruct.

Q · What does it actually mean to say a parser "derives" a program, and why does the order of the rewrites matter?
BNF & EBNF
▶ lab

Two notations for the same grammars. EBNF adds repetition, option and grouping operators that remove the recursion boilerplate — and in doing so, quietly stops telling you which way a list associates.

Q · What does EBNF actually buy me over plain BNF, and what does the shorter notation stop telling me?
Context-Free Grammars

One nonterminal on the left-hand side, and no ability to look at the surroundings. That single restriction is what makes efficient parsing possible — and what makes "declared before use" someone else's problem.

Q · What can a context-free grammar express that a regular expression cannot, and where does it run out?
Ambiguous Grammars
▶ lab

A grammar is ambiguous when one token sequence has two parse trees. `1 + 2 * 3` reading as both 9 and 7 is the toy case; the dangling `else` is the one that shipped in C, and both are fixed the same three ways.

Q · What exactly makes a grammar ambiguous, and what are my options once it is?
Operator Precedence
▶ lab

Precedence is the answer to "which operator gets to be the parent". `1 + 2 * 3` builds as `+(1, *(2, 3))`, and every mechanism for arranging that — grammar layers, declaration tables, binding powers — is producing the same tree by a different route.

Q · Where does operator precedence actually live — the grammar, the parser, or a table — and how do I check what my language does?
Associativity
▶ lab

Precedence handles two different operators; associativity handles two of the same. `a - b - c` is `(a - b) - c` in every language you use, and `2 ** 3 ** 2` is 512 in Python and 64 in MATLAB — the same operator, associating opposite ways.

Q · Why does `a - b - c` group to the left, and which operators do not?
Left Recursion
▶ lab

The rule that makes `-` group correctly is the same rule that makes a recursive-descent parser call itself forever. LR parsers prefer it, top-down parsers cannot survive it, and the standard fix trades a grammar rewrite for a loop that folds left by hand.

Q · Why does `expr → expr "+" term` hang a recursive-descent parser but not an LR parser, and how do I get rid of it?